One KACE K1000 major version after, my old article about How to use the K1000 Inventory API (WSAPI)  needed a revamp.

First of all: what is the Inventory API and what can you do with it?
  • The Inventory API is a web service of the KACE K1000 appliance that allows you to programatically send a XML file that represents a device inventory.
Using the API you can write a program that sends a series of XML files, each one representing a machine inventory, to the K1000.
The XML file format is explained in the K1000 online help: look for Valid XML schema for Windows 
For Linux and Apple devices there is a different schema that's available in the online help as well.

There are situations where you cannot install an agent and you cannot use the agentless inventory functionality but you still like to add these devices to the K1000 inventory and you need some degree of automatism: in this case the WSAPI is what you are looking for.

The inventory API endpoint is this one: http://k1000-hostname/service/wsapi.php

The documentation explains us that we can basically perform three operations with it:

  1. Ask for a session key (a challenge key): this is fundamental and we will need it for all our subsequent requests.
  2. Ask the K1000 to generate for us a KUID: this is very useful if we do not have a way to generate a KUID for an agentless machine.
    A KUID is at the end a GUID and we can even obtain it with this simple PowerShell line:
     $guid = ([guid]::NewGuid()).Guid.toupper() 
    but if we want the K1000 can do the job for us.
  3. Submit a XML file that contains the inventory information.
    The documentation is generous about this point: in the K1000 help file is explained in detail the format of this file.
    If you want to obtain a more generous blueprint of it you can even use this command line from the Kace agent directory:
     KInventory -machine -out .\myNiceFile.xml

In the documentation there is even a nice Perl script but due to the fact I'm allergic to it (I hate the needs of external libraries) I thought to write an example in PowerShell for the benefit of all the Microsoft lovers.

Some notes about it:

  • This script is given AS IS. Please revise it CAREFULLY before to execute it.
  • You're welcome to improve it and send me comments to this post.
  • To run an unsigned PowerShell script you need to ''relax'' your Execution Policy or sign it. There is a nice article about how to sign a PowerShell script here: How to sign a PowerShell script

And now the script!

You will need to pass to it three parameters: the file to send (better if included in " " ), the K1000 host name or IP and the API password.
Invoking it without parameters (or incomplete parameters) will prompt a couple of usage line. 
I tried to comment the script but if something is not clear please do not hesitate to contact me.

Sometimes ITNinja may break the correct formatting of a script. 

In this case you can download it from: https://dl.dropboxusercontent.com/u/93688199/TestWSAPIv4.ps1


 <# 
 .SYNOPSYS
This script demostrates how to use the K1000 INVENTORY API (WSAPI)
 .DESCRIPTION
 TITLE:       TestWSAPIv4.ps1
 VERSION:   4.0
 AUTHOR:      By Marco S. Zuppone for DELL KACE
 This script demonstrates how to use the K1000 INVENTORY API (WSAPI)
 .NOTES       
  You will need PowerShell 2.0 or better
 WARNING: This script is given AS IS. Use it at your own risk! Revise it carefully before to use it.
 .EXAMPLE
 TestWSAPI.ps1 [file-to-send.xml] [K1000 Hostname] [API Password]
#>
function Get-MD5String([string]$s

#This function calculates the MD5 of a string and returns the hexacecimal representation of it in lowercase.
    $result2 = ""
    $algo = [System.Security.Cryptography.HashAlgorithm]::Create("MD5")
    $hashByteArray2=$algo.ComputeHash($([Char[]]$s))
    foreach ($byte in $hashByteArray2
{ $result2 += “{0:x2}” -f $byte }
    $result2
}
if ($args.Length -lt 3)
{
Write-Host "USAGE: TestWSAPI.ps1 [file-to-send.xml] [K1000 Hostname] [API Password]" -ForegroundColor Green
Write-Host "Example: .\TestWSAPI.ps1 ""d:\myfile.xml"" k1000.mycompany.org myPassword"-ForegroundColor Green
Break
}
else 
{
if (-not (Test-Path $args[0] -PathType Leaf)) 
{
Write-Host "the file "$args[0]" does not exist or is not accessible"
Break 
}
}
New-Variable -Name password -Value $args[2] -Option ReadOnly #This is the API password you specified in the Security Settings
New-Variable -Name K1000Url -Value ("http://"+$args[1]) -Option ReadOnly #This is the K1000 url.
<#First request
In this first request we ask to the K1000 to provide us a session key.
We will need to save the cookies that the K1000 will send us back to re-use them in the subsequent requests.
#>
$uploadstring=$K1000Url+"/service/wsapi.php?keyreq=true"
$req = [System.Net.WebRequest]::Create($uploadstring);
$myCookiesContainer=New-Object System.Net.CookieContainer #We need to grab the cookies from the first request to mantain the session.
$req.Method ="GET";
$req.ContentType = "text/xml";
$req.useragent="User-Agent: Mozilla/4.0 (compatible; MSIE 7.0;)" #Let try to resemble a real browser
$req.CookieContainer=$myCookiesContainer #We need to grab the cookies from the first request to maintain the session.
$stOut = new-object System.IO.StreamWriter($req);
$resp = $req.GetResponse();
$reader = new-object System.IO.StreamReader($resp.GetResponseStream());
$session_string=$reader.ReadToEnd();
Write-Host "Session string returned from K1000: " $session_string
$passwordMD5=Get-MD5String($password.ToString())
$tokenMD5=Get-MD5String($session_string+"|"+$passwordMD5) #I calculate the reply token as specified in the K1000 help file.
Write-Host "Token to be used to reply to the K1000: " $tokenMD5
<#Second request
Now we ask the K1000 to generate for us a KUID.
This can be used to upload the inventory of a new agentless machine.
In this example we will not use it but if this request will be succesfull it means that our password is good and we calculated the reply in a good way.
So it is a good test to see if we are doing the right request.
#>
$downloadString=$K1000Url+"/service//wsapi.php?req=newuuid&key="+$tokenMD5
$req = [System.Net.WebRequest]::Create($downloadString);
$req.CookieContainer=$myCookiesContainer #We send back the cookies obtained in the first session
$req.Method ="GET";
$req.ContentType = "text/xml";
$req.useragent="User-Agent: Mozilla/4.0 (compatible; MSIE 7.0;)"
$resp = $req.GetResponse();
$reader = new-object System.IO.StreamReader($resp.GetResponseStream());
$theNewKUID=$reader.ReadToEnd();
Write-Host "The new KUID generated by K1000 using WSAPI call is " $theNewKUID;
<#Third request
Now the tricky part :-) !!!
We send to the K1000 an XML file with the format described in the documentation.
To ease the test I'm using the file I found in the documentation of the K1000
You need to specify the KUID it it's not specified in the XML file.
If you specify here a KUID and there is a different one in the file the one in the file wins.
#>
$sendFileUri=$K1000Url+'/service/wsapi.php?req=loadxml&key='+$tokenMD5+'&KUID='+$theNewKUID+'&version=6.0'
$req = [System.Net.WebRequest]::Create($sendFileUri);
$req.Method ="POST";
$req.ContentType = "text/xml";
$req.useragent="User-Agent: Mozilla/4.0 (compatible; MSIE 7.0;)"
$req.CookieContainer=$myCookiesContainer #We send back the cookies obtained in the first session
$req.ProtocolVersion="1.0"
$uploadFilePath=((split-path $args[0] -Resolve) + "\" +(split-path $args[0] -Leaf))
$FileContens = [System.IO.File]::ReadAllBytes($uploadFilePath) #I need a byte array. Potentially is the same as Get-Content $uploadFilePath -Raw -Encoding Byte
$stOut = new-object System.IO.StreamWriter($req.GetRequestStream())
$stOut.Write($FileContens,0,$FileContens.Length)
$stOut.Flush()
$stOut.Close()
$resp = $req.GetResponse();
$reader = new-object System.IO.StreamReader($resp.GetResponseStream());
Write-Host "Optional reply of the K1000: "$reader.ReadToEnd();
Write-Host "Status code and Status description of the HTTP reply from the K1000:" $resp.StatusCode " " $resp.StatusDescription
$resp.close()
$resp.Dispose()
# a bit of cleanup is a very good idea if you want to use the PowerShell debugger in the ISE
Remove-Variable K1000Url -Force 
Remove-Variable password -Force