Pull Info From LogMeIn RSS Feed?

by Ricky Bobby at 2012-08-21 11:47:32

Ok, I have a bit trickier one, at least in the sense that it’s been hard to find what I need from it.

We have an RSS feed of all of our clients computers, supplied by LogMeIn.com. I have a need to pull out all of the host names and store in a db, text file, array, what-have-you. So, I turned to invoke-webrequest, specifically:

PS> ([xml](Invoke-WebRequest "https://secure.logmein.com/usershortcut.asp?key=00_8npatgpepqck2yipcimg0c...f&profileid=2362945&showofline=1&lmiextensions=1").content).rss.channel.item

Which worked in the sense of a big dump of info, but not anything I was looking for.

So then I did the above with get-member (think that’s correct), but none of the MemberTypes I saw seemed to be what I’m after. Here’s what that command gave me:

TypeName: System.Management.Automation.PSParameterizedProperty

Name MemberType Definition
---- ---------- ----------
Copy Method System.Management.Automation.PSMemberInfo Copy()
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
Invoke Method System.Object Invoke(Params System.Object arguments)
InvokeSet Method void InvokeSet(System.Object valueToSet, Params System.Object arguments)
ToString Method string ToString()
IsGettable Property bool IsGettable {get;}
IsInstance Property bool IsInstance {get;}
IsSettable Property bool IsSettable {get;}
MemberType Property System.Management.Automation.PSMemberTypes MemberType {get;}
Name Property string Name {get;}
OverloadDefinitions Property System.Collections.ObjectModel.Collection[string] OverloadDefinitions {get;}
TypeNameOfValue Property string TypeNameOfValue {get;}
Value Property System.Object Value {get;set;}

Any ideas?
by MattG at 2012-08-21 14:30:34
As a sanity check, try the following and see if you get different results:

[script=powershell]$WebClient = New-Object Net.WebClient
$Xml = [xml] $WebClient.DownloadString('https://secure.logmein.com/usershortcut.asp?key=00_8npatgpepqck2yipcimg0c...f&profileid=2362945&showofline=1&lmiextensions=1')
$Xml.rss.channel.item | Get-Member -MemberType Property[/script]
The type returned should be a System.Xml.XmlElement object.
by Ricky Bobby at 2012-08-21 17:01:00
With that I get:

[code2=powershell]
TypeName: System.Management.Automation.PSParameterizedProperty

Name MemberType Definition
---- ---------- ----------
IsGettable Property bool IsGettable {get;}
IsInstance Property bool IsInstance {get;}
IsSettable Property bool IsSettable {get;}
MemberType Property System.Management.Automation.PSMemberTypes MemberType {get;}
Name Property string Name {get;}
OverloadDefinitions Property System.Collections.ObjectModel.Collection[string] OverloadDefinitions {get;}
TypeNameOfValue Property string TypeNameOfValue {get;}
Value Property System.Object Value {get;set;}


[/code2]
by Ricky Bobby at 2012-08-23 08:13:16
I was able to finally get some of the properties I was after listed, but not actually enumerated with:

([xml](Invoke-WebRequest "https://secure.logmein.com/usershortcut.asp?key=00_8npatgpepqck2yipcimg0cby6...gplrtbf&profileid=2362945&showoffline=1&lmiextensions=1").content).rss.channel.item | get-member

Which gave me:


TypeName: System.Xml.XmlElement

Name MemberType Definition
---- ---------- ----------
ToString CodeMethod static string XmlNode(psobject instance)
AppendChild Method System.Xml.XmlNode AppendChild(System.Xml.XmlNode newChild)
Clone Method System.Xml.XmlNode Clone(), System.Object ICloneable.Clone()
CloneNode Method System.Xml.XmlNode CloneNode(bool deep)
CreateNavigator Method System.Xml.XPath.XPathNavigator CreateNavigator(), System.Xml.XPath.XPathNavig...
Equals Method bool Equals(System.Object obj)
GetAttribute Method string GetAttribute(string name), string GetAttribute(string localName, string...
GetAttributeNode Method System.Xml.XmlAttribute GetAttributeNode(string name), System.Xml.XmlAttribute...
GetElementsByTagName Method System.Xml.XmlNodeList GetElementsByTagName(string name), System.Xml.XmlNodeLi...
GetEnumerator Method System.Collections.IEnumerator GetEnumerator(), System.Collections.IEnumerator...
GetHashCode Method int GetHashCode()
GetNamespaceOfPrefix Method string GetNamespaceOfPrefix(string prefix)
GetPrefixOfNamespace Method string GetPrefixOfNamespace(string namespaceURI)
GetType Method type GetType()
HasAttribute Method bool HasAttribute(string name), bool HasAttribute(string localName, string nam...
InsertAfter Method System.Xml.XmlNode InsertAfter(System.Xml.XmlNode newChild, System.Xml.XmlNode...
InsertBefore Method System.Xml.XmlNode InsertBefore(System.Xml.XmlNode newChild, System.Xml.XmlNod...
Normalize Method void Normalize()
PrependChild Method System.Xml.XmlNode PrependChild(System.Xml.XmlNode newChild)
RemoveAll Method void RemoveAll()
RemoveAllAttributes Method void RemoveAllAttributes()
RemoveAttribute Method void RemoveAttribute(string name), void RemoveAttribute(string localName, stri...
RemoveAttributeAt Method System.Xml.XmlNode RemoveAttributeAt(int i)
RemoveAttributeNode Method System.Xml.XmlAttribute RemoveAttributeNode(System.Xml.XmlAttribute oldAttr), ...
RemoveChild Method System.Xml.XmlNode RemoveChild(System.Xml.XmlNode oldChild)
ReplaceChild Method System.Xml.XmlNode ReplaceChild(System.Xml.XmlNode newChild, System.Xml.XmlNod...
SelectNodes Method System.Xml.XmlNodeList SelectNodes(string xpath), System.Xml.XmlNodeList Selec...
SelectSingleNode Method System.Xml.XmlNode SelectSingleNode(string xpath), System.Xml.XmlNode SelectSi...
SetAttribute Method void SetAttribute(string name, string value), string SetAttribute(string local...
SetAttributeNode Method System.Xml.XmlAttribute SetAttributeNode(System.Xml.XmlAttribute newAttr), Sys...
Supports Method bool Supports(string feature, string version)
WriteContentTo Method void WriteContentTo(System.Xml.XmlWriter w)
WriteTo Method void WriteTo(System.Xml.XmlWriter w)
Item ParameterizedProperty System.Xml.XmlElement Item(string name) {get;}, System.Xml.XmlElement Item(str...
description Property System.Xml.XmlElement description {get;}
guid Property System.Xml.XmlElement guid {get;}
link Property System.Xml.XmlElement link {get;}
lmihostinfo Property System.Xml.XmlElement lmihostinfo {get;}
pubDate Property System.Xml.XmlElement pubDate {get;}
title Property System.Xml.XmlElement title {get;}



Seeing that I was after the last 6 properties, as a test I added on:

| select description

But so far, it’s blank and I just get back:

description
-----------







Any ideas?
by poshoholic at 2012-08-23 10:38:16
I think you need to break your pipelines down into multiple steps until you identify the root issue here so that you can see what is going on every step of the way.

For example, step 1 would be to retrieve the rss feed. Step 2 would be to convert that to xml. Step 3 would be to interrogate the xml interactively so that you can find what you are looking for.

Something like this is what I have in mind:

[script=powershell]$webRequestResult = Invoke-WebRequest "LogMeIn.com - Invalid rss shortcut
$rssXml = [xml]$webRequestResult.content[/script]

Then once you have that in place, you should be able to visualize your xml in PowerShell, see the data, using this sequence of commands:

[script=powershell]$rssXml.rss
$rssXml.rss.channel
$rssXml.rss.channel.item[/script]
In simple xml browsing like this, each step of the way, you should expect to see the properties that you will use in the next step.

I think it might be easier to help you if you take this approach and identify at what point you are unable to get the xml data you need. It would also be useful if you could share a sample of the xml you are working with by writing the xml to a file and pruning it back to one entry with any data you want removed stripped from that entry.
by Ricky Bobby at 2012-08-23 13:01:19
Ok, very cool! And apologies, I’ve clearly shown how much of a noob i am at this point, but very much appreciate the help.

So, following your example, I get the following:

PS> $rssXml.rss

version channel
------- -------
2.0 channel


Ok, fine. Next up:

PS> $rssXml.rss.channel


title : LogMeIn.com - support@xyz.com
link : http://logmein.com/
image : image
description : LogMeIn Computers for support@xyz.com
language : en-us
pubDate : pubDate
lastBuildDate : lastBuildDate


Cool to see what I can enumerate, though maybe a little troubling that not all pieces are filled out. Anyway, on to the next:

PS> $rssXml.rss.channel.item


title : title
description : description
link : link
pubDate : pubDate
guid : guid
lmihostinfo : lmihostinfo

title : title
description : description
link : link
pubDate : pubDate
guid : guid
lmihostinfo : lmihostinfo

title : title
description : description
link : link
pubDate : pubDate
guid : guid
lmihostinfo : lmihostinfo

on and on and on and on . . . which matches up with how many machines we have in LMI, but . . .that seems to be the issue, those items aren’t populated, which is what I"m ultimately after.

Of course I can’t tell if that’s an issue with PoSH or an issue with LogMeIn - though I’d think not the latter if I can see the correct info in an RSS reader just fine . . . what make you of this?
by Ricky Bobby at 2012-08-23 13:45:30
Also, here’s an XML example:

<item>
<title><![CDATA[_ABCXYZ Server (offline)]]></title>
<description><![CDATA[Click to connect to _ABCXYZ Server.]]></description>
<link><![CDATA[https://secure.logmein.com/mycomputers_connectrss.asp?key=00_8npatgpepqck2...u7rvqa7qvxsgplrtbf&hostid=59896197]]></link>
<pubDate><![CDATA[Thu, 23 Aug 2012 20:10:22 UTC]]></pubDate>
<guid><![CDATA[https://secure.logmein.com/mycomputers_connectrss.asp?key=00_8npatgpepqck2...u7rvqa7qvxsgplrtbf&hostid=59896197]]></guid>


<lmihostinfo>
<description><![CDATA[_ABCXYZ Server]]></description>
<status><![CDATA[0]]></status>
<link><![CDATA[https://secure.logmein.com/mycomputers_connectrss.asp?key=00_8npatgpepqck2...u7rvqa7qvxsgplrtbf&hostid=59896197]]></link>
<ip><![CDATA[xxx.xxx.xxx.xxx]]></ip>
</lmihostinfo>

</item>


So, I found that I could what I was after, at least one of the items by just adding on .lmihostinfo.xxx. Yay noob me, but especially yay skilled YOU! THANKS for pointing me in the right direction!!

So, I think . . . final question. I went back to the powers that be to find out what they need exactly, and one . . . easy enough . . . is description. The second, however, is the host ID. That’s the last piece of the "link" - of course. There must be, but is there any way to extract just that part of the URI with the route we’re headed? And secondly, "| select description,link" doesn’t seem to give me what I want exactly, so how I would I pull both at the same time?

The master plan is to dump those two pieces of info into SQL, but one step at a time.
by Ricky Bobby at 2012-08-23 13:50:00
Note: I’m thinking "split-path" will pull hostid=12345, just not sure how to word it . . . yet. :slight_smile:
by poshoholic at 2012-08-23 14:57:04
Excellent, I’m glad you’re figuring this out. Helping hands are great, but there is no substitute for getting to the bottom of these issues through your own investigation – you learn, and retain, much more that way.

Split-Path isn’t quite what you want, but you’re on the right track. There is a Split method on strings that can split a string on characters to break it up. That’s one way of doing it. Assuming you store your link in a variable called $link, you could split it on ?, &and = characters so that you separate out the parameters of the URL, like this:

PS C:&gt; $link.Split([char]@(‘?’,‘&’,‘=’))
https://secure.logmein.com/mycomputers_connectrss.asp
key
00_8npatgpepqck2...u7rvqa7qvxsgplrtbf
hostid
59896197


Then you just need to take the last one of that array and you’ll have your host id, like this:

PS C:&gt; $link.Split([char]@(‘?’,‘&’,‘=’))[-1]
59896197


The only concern I have there is whether or not hostid will always be at the end of the URL parameters. If not, you’ll have to do more work here to make sure you find the integer value that follows hostid in the array that is returned.

Another way which is more robust for this sort of thing because it isn’t dependent on hostid being at the end of the URL parameters is to use the regular expression -replace operator. Regular expressions incur a much higher learning curve, but they are crazy powerful and allow you to get at just the item you need in a single command once you know what you’re doing. I could leave it up to you to figure out, but regular expression matching is a more difficult concept to grasp, so it will be better if I give you this one and explain it so that it’s at least familiar to you when you run into it again.

Let’s assume again you have your link in a variable called $link, with this value:
https://secure.logmein.com/mycomputers_connectrss.asp?key=00_8npatgpepqck2...u7rvqa7qvxsgplrtbf&hostid=59896197

To pull the hostid numeric value from that, you can use -replace, like this:

PS C:&gt; $link -replace ‘^.+&hostid=(\d+)(&.+)?$’,‘$1’
59896197


This -replace operator use breaks down like this:
With the string $link, find a sequence of one or more numbers (that’s the \d+ part; \d represents a number in regular expressions, and + means one or more in sequence) that is either at the end of a string (the $ in regular expressions marks the end of the string) or that is followed by an & and one or more characters (the ‘(&.+)?’ part identifies the ampersand, . represents any character, + means one or more in sequence, and having that in brackets with a ? afterwards makes it optional) and that follows one or more of any character at the start of the string (that’s the .+ part in the first part of our search string, and ^ indicates the start of the string), followed immediately by ‘&hostid=’ (the literal part of our search, used to get us to the right spot), and capture that sequence (by wrapping \d+ in brackets, we’re telling the regular expression engine to capture the numeric value so that we can use it later). Replace the string that matches (the entire string, because we searched from front – ^ – to back – $) with the first captured match (‘$1’, our decimal sequence that we captured during the search).

Like I said, Regular Expressions are really, really powerful because they allow you to perform pattern matching while you do search and replace operations on complicated strings. But they can be incredibly complicated (there are examples that are easier to follow than this one). Most people start using regular expressions by finding an example that someone gives them that works (which gives them exposure to it), and then over time looking at the easier examples so that they can figure out some simple ways to use it on their own.
by Ricky Bobby at 2012-08-23 15:05:21
About to head out of the office for a moto ride (brain friiiied, haha), but yes, was just looking at RegEx as another option and going "oh f***". Cool for the learning piece, but . . .wow.

Anyway, if it helps you to know, I’ve done the following:

$lmidescript = $rssXml.rss.channel.item.lmihostinfo.description
$lmiguid = $rssXml.rss.channel.item.guid


So I have them in tidy variables at least. Running either by itself gives me exactly what I’m after. In the case of $lmiguid, the hostid is always at the end, so I think I’m safe there for now (for this project anyway).

And, that’s all my brain can do at the moment, but I’ll be re-reading the above in depth and trying more stuff out later tonight. Again, really, apprecaite your help Krik - this is exactly the kind of stuff I need to get learning and stick with it vs. how I’ve tinkered with it here and there over the years but had no practical use for it.

Anyway, vrooooom! :slight_smile:
by Ricky Bobby at 2012-08-23 20:40:12
K, just started back in and used your first (non-regex) example.

PS> $lmiguid.Split([char]@(‘?’,‘&’,‘=’))
Method invocation failed because [System.Xml.XmlElement] doesn’t contain a method named ‘Split’.
At line:1 char:26
+ $lmiguid.Split([char]@(‘?’,‘&’,‘=’))
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Split:String) , RuntimeException
+ FullyQualifiedErrorId : MethodNotFound


Hmmm, ok, maybe my variable is bad somehow. Wouldn’t think so, but let’s back up:

PS> $rssXml.rss.channel.item.guid.Split([char]@(‘?’,‘&’,‘=’))
Method invocation failed because [System.Xml.XmlElement] doesn’t contain a method named ‘Split’.
At line:1 char:47
+ $rssXml.rss.channel.item.guid.Split([char]@(‘?’,‘&’,‘=’))
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (Split:String) , RuntimeException
+ FullyQualifiedErrorId : MethodNotFound


Nope. Same thing. Going to dig in and see what I can find out, but any pointers are great too (quick Google wasn’t super helpful).
by Ricky Bobby at 2012-08-23 23:44:44
Ok, so the noob (me) has spent the past several hours on it, but gotten no further. Isolating the hostid is for extra brownie points for this work project, but now I’m just stuck on matching up the description (computer name) with the guid (long URI with hostid at the end) and exporting it to a table or csv or something. First thought after the .split and regex examples didn’t work for me was going back to "select" in some way, but no dice.

For the record, the below is that I have currently:

$webRequestResult = Invoke-WebRequest "https://secure.logmein.com/usershortcut.asp?key=00_8npatgpepqck2yipcimg0cby6jrez4ec...45&showoffline=1&lmiextensions=1"
$rssXml = [xml]$webRequestResult.content
$lmidescript = $rssXml.rss.channel.item.lmihostinfo.description
$lmiguid = $rssXml.rss.channel.item.lmihostinfo.link


I can get both to export to host invidually, and of course CSV individually, and all look good, but I think I’ve exceeded my PoSH knowledge especially when throwing XML and nodes and elements into the mix - at least for tonight anyway. Going to sleep on it and maybe let me subconscious have an "oh duh" moment. All the same, if any nudges in the right direction are available, much appreciated.

Also for the record, I’ve looked into XPath, and nodes, and how maybe something like the below, tweaked for my needs, would work for my case, but now I’m not sure if I’m getting hotter or colder.

Yeah, brain done. Bed. Thanks again for all the help! :slight_smile:

$nodelist = $xd.selectnodes("/printDrivers/printDriver") # XPath is case sensitive
foreach ($printerDriverNode in $nodelist) {
$XMLid = $printerDriverNode.getAttribute("id")
$XMLinf = $printerDriverNode.selectSingleNode("inf").get_innerXml()
$XMLdriverPath = $printerDriverNode.selectSingleNode("driverPath").get_innerXml()
$printerPathNodeList = $printerDriverNode.selectNodes("printerPaths/printerPath")
foreach ($printerPathNode in $printerPathNodeList) {
$XMLprinterPath = $printerPathNode.selectSingleNode("printerPath").get_innerXml()
}
}
by poshoholic at 2012-08-24 07:45:20
There’s one piece you’re missing. When you do this:

[script=powershell]$lmiguid = $rssXml.rss.channel.item.lmihostinfo.link[/script]
You end up with a System.Xml.XmlElement object, as can be seen by doing this:

[script=powershell]$lmiguid.GetType().FullName[/script]
The Split method I was talking about is for System.String objects, so you need to get the string that is inside the CDATA section. When you output your $lmiguid object, did you notice the results appear in a table, with the string you want under a heading called "#cdata-section"? When you see tabular output, or a list output when there are a lot of properties with property-value pairs, that’s an indication that you’re working with objects with properties (as opposed to objects with simple values like strings, integers, etc.). Sure, strings have properties such as Length, but they are generally simple objects to use with single values. To get the string value (which you can then split), you need to reference the "#cdata-section" property of the link XML Element. You can do that like this:

[script=powershell]$lmiguid = $rssXml.rss.channel.item.lmihostinfo.link.'#cdata-section'[/script]
Note the quotes around the "#cdata-section" property. Those quotes are necessary only because that property name contains a hash symbol ("#"), which is a special character in PowerShell used to mark the beginning of a comment. By putting it in quotes, we’re telling PowerShell that it is a string that is used to reference a property name. If the property name had spaces in it, we would have had to use quotes for that as well. There are a few other examples when you need quotes around property names, but spaces and the "#cdata-section" property are probably among the most common examples of this.

Once you get the string and just the string in your $lmiguid variable, then you can use the Split method to break it up.

Does all of that make sense?
by poshoholic at 2012-08-24 07:48:20
Also note that all of the strings you want in your XML document are inside CDATA sections, so you’ll have to use the "#cdata-section" property for any of the text strings you need.
by Ricky Bobby at 2012-08-24 07:50:43
Yup. I had tried targeting cdata but of course not in the most obvious way (quotes) - duh!

Just tried and it gave me what I was after! AWESOME!!

Now, I’m going to try to see if I can figure out how to get them all to output paired up. :slight_smile:
by Ricky Bobby at 2012-08-24 08:13:51
Ok, so if i do [-1] at the end of the split, it gives me one of those hostid’s, but as there’s 900+ . . . I’m thinking I need to foreach it, but not sure on syntax exactly.
by poshoholic at 2012-08-24 09:19:30
You need to break it out in the locations where it becomes a collection you want to enumerate or perform multiple operations on. It would end up being something along these lines:

[script=powershell]foreach ($item in $rssXml.rss.channel.item) {
$linkUrl = $item.lmihostinfo.link.'#cdata-section'
$linkUrl.Split(…)
}[/script]
by Ricky Bobby at 2012-08-24 10:23:49
Awesome, does as expected! I feel like a bit of a loser on this one though honestly. I’ve been wracking my brain and trying different things to get both $lmidescript and "$lmiguid" to output to host together in a table, but . . . oof. If I put "$lmidescript = $rssXml.rss.channel.item.lmihostinfo.description" IN the foreach loop it seems to just ignore it and dump out the hostid.

My thinking of doing that is that foreach is iterating through each item and returning what was defined/asked for in there, but apparently that’s not the case - or it’s ignoring the description with or without "#cdata-section" specified.

Next thought is, ok, store that foreach in it’s own array to later be called later:

$hostiddeployid = foreach ($item in $rssXml.rss.channel.item) {
$lmiguid = $item.lmihostinfo.link.‘#cdata-section
$lmiguid.Split([char]@(‘?’,‘&’,‘=’))[-1,-3]
}


(Note, I’m told they may also want the deploy id (or meat of the URI) so I’ve put that in as well.)

Seems to work fine, $hostdeployid at the prompt returns what I was after. Now, ok, how do I call both and output so they’re paired up? Hmmmm . . . apparently you can make two variables into their own variable or array (correct term?). So after the close of foreach, let’s try:

$lmiunit = $lmidescipt + $hostiddeployid
$lmiunit


Nope. Just gives me the deployid and hostid, no description. Hmmmm, ok, what about:

$lmiunit = $lmidescipt,$hostiddeployid
$lmiunit


Hmmm, nope, same thing. Grrrrr.

All of my help file searching and googling has thus far turned up nothing either though I’m sure it’s just the matter that I’m not sure the correct term to search for.
by Ricky Bobby at 2012-08-24 10:38:28
Pulling out the $hostiddeployid which made the foreach a variable and then doing:

"$lmiguid $lmidescript"

Sorta worked, but it appears to list all of the deploy and hostid’s first, and THEN all of the description/names.

I’ve also tried making a function, but no dice.

EDIT: Same thing when doing the above with , or +
by Ricky Bobby at 2012-08-24 10:49:17
Also just discovered concatenation via += and tried:

$hostdeployid = $lmiguid +=$lmidescript
$hostdeployid


Same still. :frowning:

By the way, I definitely welcome help, but I find it helps to type this stuff out sometimes even if I’m just talking to myself. :slight_smile:
by poshoholic at 2012-08-24 12:39:36
First, regarding getting the description to display, I can explain why you aren’t seeing it. In your foreach loop, you were pulling the description out of the xml and you were storing it in a variable. That’s all. PowerShell was simply doing as you told it. To display something in PowerShell, let it "fall through". You can either not store it in a variable, in which case PowerShell will send it to standard output (the host), or you can store it and then invoke the variable name $lmidescript on a line by itself and PowerShell will write the value of that variable to the host.

Regarding combining the different pieces of data together, this is a very common use case. What you want is a custom object with properties so that PowerShell shows the results in a table.

Consider this example:

[script=powershell]$myObject = New-Object -TypeName PSObject -Property @{
Description = $lmidescript
HostId = $lmiguid
}[/script]
Then you can display that object by simply outputting it, like this:

[script=powershell]$myObject[/script]
You even get column names that match the property names you specified, and you can add more properties as necessary.

If you’re using PowerShell 3.0 or later, this gets even easier, where you can simply do this:

[script=powershell]# NOTE: This block requires PowerShell 3.0 or later.
$myObject = [PSCustomObject]@{
Description = $lmidescript
HostId = $lmiguid
}[/script]

So, you need to update your foreach loop to do two things:
1. Create the custom object.
2. Output the custom object.

You can even accomplish both at once if you don’t store the custom object in a variable (in which case it will fall through to the standard output).
by Ricky Bobby at 2012-08-24 12:42:44
Ok. Just had a long conference call and I"m stuck. Ultimately here’s the project now -

Return KEY
Return HOSTID
Return Description (aka name)
Return InstallDate (which is pubdate)

All in one file, likely CSV.

I can get the first two as they are part of the same string.
I can get the 3rd one by itself, but not in a table/with the above two.
I can get some date via pubdate, but it just appears to be maybe the start of my PS session as all 900 dates are the same (yesterday), which is definitely not the install date.

Below is lmiinfo:

PS> $rssxml.rss.channel.item.lmihostinfo

description status link ip
----------- ------ ---- –
description status link ip


Gonna go do something else at this point as I’m out of ideas.
by poshoholic at 2012-08-24 12:48:06
If you review my last reply, that shows you how to get all of your properties in a single object, which can be export as a csv by using Export-Csv. That’s a simple item to add to the pipeline at the end of your script.

The only complication it seems then is getting the install date. By install date, are you looking for the date that the OS was installed on these systems? Right off the bat, sounds like you would need to harvest the IP address of those systems and then use Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ipAddress to get that information. The object that command returns has an InstallDate property on it, in a WMI date time format. You can convert it to a standard date-time format though, I have script for that.
by Ricky Bobby at 2012-08-24 12:56:27
[quote="poshoholic"]If you review my last reply, that shows you how to get all of your properties in a single object, which can be export as a csv by using Export-Csv. That’s a simple item to add to the pipeline at the end of your script.

The only complication it seems then is getting the install date. By install date, are you looking for the date that the OS was installed on these systems? Right off the bat, sounds like you would need to harvest the IP address of those systems and then use Get-WmiObject -Class Win32_OperatingSystem -ComputerName $ipAddress to get that information. The object that command returns has an InstallDate property on it, in a WMI date time format. You can convert it to a standard date-time format though, I have script for that.[/quote]

What’s frustrating me at this point is that each piece on it’s own make sense, but putting the whole puzzle together is killing me. lol and :frowning:

In the foreach does $myobject go before or after foreach (I’m googling for examples to go by)?

And as for the date, just took a closer look at the RSS feed in a browser . . .they have the same date issue as the PoSH command does. I guess pubdate is when the feed was pulled/read, nothing more. But to answer your question, we are just after the LogMeIn install date, may just not be available. Or we’ll have to dig in to the registry, but . . . I’d be happy to just get my head around the foreach and object stuff first.
by Ricky Bobby at 2012-08-24 13:25:12
I can decide to laugh or cry. It’s like when there’s a word on the tip of your tongue as to how close I am to getting/understanding this! Hahahah/grrrrrrr.

So, part of what’s making it harder is that I get an output of 900+ lines every time, which is fine, but also means I miss things scrolling by. I took another closer look at the output of your new-object code verbatim (just stuck it at the end after the "}" to see what happened), and after the usual key and hostid scroll, at the very bottom was a table with just two items. The key, unsplit, and then "description" with what looked to be every computer name. I tried | fl to see if maybe it would dump it out that way and it was all there, but no dice, it’s just 1 HOSTID and what is probably all of the computer names. HOWEVER, that gives me a clue as to where I need to head with it. Baby steps, baby steps, lol. This is probably one of the simplest things in the world to most people reading, but … me brain hurt bad.
by poshoholic at 2012-08-24 14:09:30
This isn’t one of the simplest things in PowerShell, at all. You’re parsing XML – that’s not easy. You’re dealing with properties with names that you have to put quotes around – not for beginners. You’re doing some complicated string parsing to get the data you need – not easy either. Dealing with an object-oriented language, creating custom objects, adding items to a collection, processing a collection of 900 objects, wrapping your head around loops, variables, and so on. This is not easy for someone new to PowerShell. It’s easy for me, but I’ve been developing software for 15 years, 5 of which with PowerShell, so I do this for a living. But it’s not easy when you’re just starting out.

When helping people with solutions, I hold back sometimes on just pushing the solution in place on forums so that I’m not stopping you from figuring it out on your own. But I can and am happy to help more.

Putting the custom object creation inside the foreach and exporting it as a csv file would look like this:

[script=powershell]$hostInfoArray = @()
foreach ($item in $rssXml.rss.channel.item) {
# First, pull out the link URL
$lmiLink = $item.lmihostinfo.link.'#cdata-section'
# Then the description
$lmiDescription = $item.lmihostinfo.description.'#cdata-section'
# Now break the url up into sections so that we can harvest the key and the host id
$linkSections = $lmiLink.Split([char]@('?','&','='))
# Now create a new object which has the Key, HostId and Description values as properties
$hostInfo = New-Object -TypeName PSObject -Property @{
Key = $linkSections[-3]
HostId = $linkSections[-1]
Description = $lmiDescription
}
# And finally put the host data in our collection
$hostInfoArray += $hostInfo
}
# Lastly, export the collection to a CSV file
$hostInfoArray | Export-Csv C]
You’re right that you’ll have to identify the LogMeIn install date by interrogating the Registry or the file system on each machine.

Also, there are some tricks when you are working with large collections that can make this easy by allowing you to work with only one or two objects.

Compare this foreach statement with your other one:

[script=powershell]foreach ($item in $rssXml.rss.channel.item | Select-Object -First 2) {
$item
}[/script]
If you run that you’re only going to get two items, not 900. It’s a good trick to keep in your pocket so that you work with a small set rather than the entire thing from the start. You can decide how many to pass to the -First parameter of Select-Object, and when you’re ready to do the whole thing you can just remove the "| Select-Object -First 2" pipeline statement.
by Ricky Bobby at 2012-08-24 15:38:21
Thanks for the kinds words and help Kirk. I’m genuinely appreciative that I had to struggle through vs. just popping on a forum and getting the answer. I’m a strong believer in learning to fish vs. just getting a bucket of fish for dinner. We have a guy at my company that constantly IMs and emails and calls me asking the dumbest things when it’s clear he’s just being lazy. :slight_smile:

I’m also a little disappointed that I didn’t get it all by myself (the above works by the way!), but still was a great learning exercise. One I’m still going to have to study a little bit, though with your tutelage I was pretty damn close (I’m not going back through the past 48 hours in my head trying to see where I was messing up exactly.

Also, I did know about the "-first", but I guess I needed select-object before that, as for me it just seemed to ignore it after my pipe and spit the whole thing out every time. Anyway, I’m going to clean it up a bit for my needs and send it off, but thanks a million again. I may have some post-mortem questions once my brain settles down as well - just to make sure I’m understanding all involved here, but will study it first, break shit on purpose to see what happens, etc before asking.

That said, I do have one that flummoxed me . . . why, if I said "export-csv c:\filename.csv" would it basically ignore it, print all to screen and then create a basically blank CSV file as well? I didn’t specify "tee" and the command otherwise looked right.

Anyway, thanks times infinity, you’re an amazing help.
by poshoholic at 2012-08-26 18:41:43
You’re welcome Ricky. :slight_smile:

Regarding your last question, since I can’t see the exact script where you had your Export-Csv issue, I’m not sure I can answer that one. I can identify probable causes though.

Export-Csv will export the collection of objects it receives to the csv file you specify. If it doesn’t receive anything, it will prompt you for something to export by asking for the value of the Input-Object parameter, but if it receives something, even an empty collection, it will export to a file. Exporting an empty collection to a csv file results in an empty csv file with a size of 0.

If you ever use Write-Host, be very careful because that writes data directly to the host and does not send it out the pipeline. Write-Host, Write-Debug, Write-Verbose, Write-Error, Write-Warning and Write-Progress should be used to communication information about the execution of the script. They should not be used to return data that you want returned for further processing or output. If you were using Write-Host, that’s one reason that would explain why you were seeing output on the screen that wasn’t getting sent to the pipeline.

Those are two possible causes that come to mind, and that’s about all I can do without seeing the script that had this undesirable behavior.

I’ll keep an eye out for your post-mortem questions as you come up with them later.