COM InternetExplorer.Application Behavior - powershell

Been wrestling with parsing the HP Warranty website since the web service method seems to be constantly changing.
Problem I am having is I have tried to use the internet explorer com and its acting different depending on a version difference somewhere either PS or IE of which I am not certain how to tell.
Here is my core portion of code that I am testing with. You would need to find an HP Serial Number to test with.
$SerialNumber = ""
$link = "http://h20564.www2.hp.com/hpsc/wc/public/find?rows%5B0%5D.item.serialNumber=" + $SerialNumber + "&rows%5B0%5D.item.countryCode=US&submitButton=Submit"
Write-Verbose "Calling: $link"
$request = New-Object System.Net.WebClient
$request.UseDefaultCredentials = "True"
$request.Proxy = [System.Net.GlobalProxySelection]::GetEmptyWebProxy()
$results = $request.downloadstring($link)
if($results -match "Base Warranty") {
Write-Verbose "Results contain Warranty Data"
$results | Out-File "C:\Temp\Warranty.html" -Encoding ascii
$oIE = New-Object -ComObject InternetExplorer.Application
$oIE.Navigate("C:\Temp\Warranty.html")
$ohtmldoc = $oIE.Document
$rows = $ohtmldoc.documentElement.getElementsByClassName("hpui-normal-row")
$data = $rows[0].ChildNodes | ? {$_.TagName -eq "td"}
$ExpireDate = Date($data[4].InnerText)
} else {
Write-Verbose "Results do not contain Warranty Data"
$ExpireDate = ""
}
Works fine on my win 10 machine with IE11 v11.212.10586.0; PSVersion 5.0
Failing win 2008 R2 Term servers with IE11 v11.0.9600.17873CO in two different fashions; PSVersion 4.0
It appears that the DOM is not populating the ChildNodes property from the $rows[0] variable after it parses the ClassName.
Aside from the above being my main issue,
Another 2008 R2 Term server as soon as I invoke the $oIE.Navigate Method it opens an instance of an IE window for the link being called then the rest of the commands fail which I haven't seen happen yet on my other two environments.
Any thoughts would be greatly appreciated as I don't often use the InternetExplorer.Application COM. I really just need to be able to parse this HTML content to get the table data effectively. I had tried ditching the COM and ingesting the HTML contents as XML but HP has several malformed lines on the page this warranty result kicks back.

Related

InternetExplorer.Application COM object's getElementById method not working

I am very new to powershell, and I am trying to automate a login to website, I have browsed many examples but NONE is working without errors.
For example with this very simple script:
$ie = New-Object -comobject InternetExplorer.Application
$ie.Visible = $true
$ie.Navigate($URL)
$ie.Navigate("www.facebook.com")
$ie.document.getElementById("pass")
$ie.document.getElementById("pass").value = "Hi"
I am supposed to get "Hi" in the password field but I get instead this
I am getting this error in ALL the examples I found, what's going on? Is this something no longer supported in Powershell?
I have windows 10.
Don't use getElementById. Use IHTMLDocument3_getElementById, e.g.:
$ie.Document.IHTMLDocument3_getElementById("email").value = "asd123"
You can do this for other methods as well, such as IHTMLDocument3_getElementsByName.
I should add that I don't know why this works - I just used the following query to find any useful looking members on the Document object and played around with them
$ie.Document | Get-Member | where Name -like '*get*'

Powershell - Can't figure out how to get web content from IE object

I am pretty new to Powershell and just using it for personal stuff. I have been experimenting with pulling specific info from websites to include in emails to family. By reading the forums I got pretty good using the Invoke-WebRequest cmdlet, but soon hit upon its limitation of not having access to content constructed dynamically at the time the page is loaded.
Thanks to these forums, I then discovered the IE object and how to pull the data. I had luck with one website, but another I tried does not work the same. Hoping for a little help figuring it out.
Here is a snippet of the inspected code for the page, with my target of interest highlighted.
Below is the code where I am trying to extract that text string. I have tried many iterations and approaches with no success. What is odd, though, the $ie.Document object supposedly has a "body" object, but when I tried to access it, I get a null object error. I notice the Document object itself has a getElementsByTagName method, so I tried that. It does not have a getElementsByClassName method.
Note that the URL I am loading is "https" so I am wondering if this is causing issues. Suggestions appreciated! If I can just get a surrounding chunk of the HTML, I am fine doing some string manipulation to get what I want.
# Create IE object and load URL
$WeatherURL = "https://weather.com/weather/today/l/77630"
$ie = New-Object -comobject "InternetExplorer.Application"
$ie.visible = $true
$ie.navigate($WeatherURL)
# Wait for the page to load
while ($ie.Busy -eq $true -Or $ie.ReadyState -ne 4) {Start-Sleep 2}
$Doc = $ie.Document
$Weather0 = $Doc.getElementsByTagName('span') `
| ?{$_.getAttribute('class') -eq "today-wx-descrip"} | Select-Object -First 1
You should replace
$Weather0 = $Doc.getElementsByTagName('span') `
| ?{$_.getAttribute('class') -eq "today-wx-description"} | Select-Object -First 1
With
$Weather0 = $Doc.getElementsByTagName('span') `
| ?{$_.getAttribute('class') -eq "today-wx-descrip"} | Select-Object -First 1
Note today-wx-description vs today-wx-descrip.

Modify the array text condition in an Outlook rule with PowerShell?

I work on a team, who manage a few hundred servers. We each take primary responsibility for about 100 servers. I am the new person on the team, so I have a rule "MyServers" in outlook that makes a special sound and moves emails in to the folder "MyServers", when an email comes in with the name of one of my servers in the subject or body. Servers come and go, and the responsible person changes occasionally. I can use the GUI to modify the list, but what I want to do is use PowerShell to modify the list of servers based on a data set from a SQL query on our table of whom belongs to what. (also would be helpful when covering for someone else.)
Per PowerShell - Managing an Outlook Mailbox with PowerShell, By Joe Leibowitz, March 2013 it is possible in theory. That article and the post Hey, Scripting Guy! How Can I Tell Which Outlook Rules I Have Created? December 15, 2009 by ScriptingGuy1 have taught me how to get outlook files into PowerShell to read and or write. The post Multiple Actions for one Outlook rule in Powershell has also been helpful.
I can find several examples of creating rules, mostly around email addresses. As I did more research (below) it seems like the I want to modify 'TextRuleCondition.Text' but I am not finding any example code that gets in to reading OR modifying rule conditions for a single existing rule.
Specifying Rule Conditions
TextRuleCondition Object (Outlook)
TextRuleCondition.Text Property (Outlook)
Optimally: I would like to go to the "MyServers" rule and change the array, from what it is to a new array that I will build with PowerShell, after getting the list from a SQL table.
##source https://blogs.technet.microsoft.com/heyscriptingguy/2009/12/15/hey-scripting-guy-how-can-i-tell-which-outlook-rules-i-have-created/
## Gets outlook rules on PC
#Requires -version 2.0
Add-Type -AssemblyName microsoft.office.interop.outlook
$olFolders = “Microsoft.Office.Interop.Outlook.OlDefaultFolders” -as [type]
$outlook = New-Object -ComObject outlook.application
$namespace = $Outlook.GetNameSpace(“mapi”)
$folder = $namespace.getDefaultFolder($olFolders::olFolderInbox)
$rules = $outlook.session.DefaultStore.<Some code here gets TextRuleCondition.Text for just MyServers>
$rules ## this displays the current value
##More code inserts the array that I built earlier (not actually built, yet as I don't know what it should look like)
$rules.Save() ## this saves the changes.
Everything I have found so far programmatically creates an entire new rule from PowerShell. Nothing indicates if it is, or is not possible to modify an existing rule. My Plan "B" would be to read the existing "MyServers" rule, modify the array, and overwrite the old rule with a new one. This is problematic as it limits options, only some conditions and actions can be created programmatically.
#setup
Add-Type -AssemblyName microsoft.office.interop.outlook
$olFolders = “Microsoft.Office.Interop.Outlook.OlDefaultFolders” -as [type]
$outlook = New-Object -ComObject outlook.application
$namespace = $Outlook.GetNameSpace(“mapi”)
#get all of the rules
$rules = $outlook.Session.DefaultStore.GetRules()
#get just the rule I care about
$myRule = $rules | ? { $_.Name -eq 'My Awesome Rule' }
#build my new array of text conditions
$textValues = #('ServerA', 'NewServerB')
#replace the existing value on my rule (this is assuming you are using BodyOrSubject, you can substitute Body, Subject, etc)
$myRule.Conditions.BodyOrSubject.Text = $textValues
#save all the rules
$rules.save()

HTMLDocumentClass and getElementsByClassName not working

Last year I had powershell (v3) script that parsed HTML of one festival page (and generate XML for my Windows Phone app).
I also was asking a question about it here and it worked like a charm.
But when I run the script this year, it is not working. To be specific - the method getElemntsByClassName is not returning anything. I tried that method also on other web pages with no luck.
Here is my code from last year, that is not working now:
$tmpFile_bandInfo = "C:\band.txt"
Write-Host "Stahuji kapelu $($kap.Nazev) ..." -NoNewline
Invoke-WebRequest http://www.colours.cz/ucinkujici/the-asteroids-galaxy-tour/ -OutFile $tmpFile_bandInfo
$content = gc $tmpFile_bandInfo -Encoding utf8 -raw
$ParsedHtml = New-Object -com "HTMLFILE"
$ParsedHtml.IHTMLDocument2_write($content)
$ParsedHtml.Close()
$bodyK = $ParsedHtml.body
$bodyK.getElementsByClassName("body four column page") # this returns NULL
$page = $page.item(0)
$aside = $page.getElementsByTagName("aside").item(0)
$img = $aside.getElementsByTagName("img").item(0)
$imgPath = $img.src
this is code I used to workaround this:
$sec = $bodyK.getElementsByTagName("section") | ? ClassName -eq "body four column page"
# but now I have no innerHTML, only the lonely tag SECTION
# so I am walking through siblings
$img = $sec.nextSibling.nextSibling.nextSibling.getElementsByTagName("img").item(0)
$imgPath = $img.src
This works, but this seems silly solution to me.
Anyone knows what I am doing wrong?
I actually solved this problem by abandoning Invoke-WebRequest cmdlet and by adopting HtmlAgilityPack.
I transformed my former sequential HTML parsing into few XPath queries (everything stayed in powershell script). This solution is much more elegant and HtmlAgilityPack is real badass ;) It is really honour to work with project like this!
The issue is not a bug but rather that the return where you're seeing NULL is because it's actually a reference to a proxy HTMLFile COM call to the DOM model.
You can force this to operate and return the underlying strings by boxing it into an array #() as such:
#($mybody.getElementsByClassName("body four column page")).textContent
If you do a Select-Object on it, that also automatically happens and it will unravel it via COM and return it as a string
$mybody.getElementsByClassName("body four column page") | Select-Object -Property TextContent

returning and referencing remote powershell variable results

I'm very new to powershell so looking some assistance. I am trying to run remote powershell script to check health of or VDI enviroment using Citrix Commandlets. (I am implementing the script on Microsoft orchestrator .Net Activity). So I have the following code:
#2012 VDI Desktop check
$vdims = "MyCitrixPowershellSDKServer"
function vdidesktopcheck
{
asnp Citrix*
$result = Get-BrokerDesktop -InMaintenanceMode $True -DesktopGroupName Unidesk
$result
}
$output = invoke-command -computer $vdims -scriptblock ${function:vdidesktopcheck}
$machinename = $output.machinename
$machinename
$state = $output.RegistrationState
$state
So when I use orchestrator to expose the variables $machinename, $state - I get the 'last' result from the involked Get-BrokerDesktop query. However Get-Brokerdesktop query may have multiple machines returned so I am hoping to be able to reference each of the machines that match the query output. Thats the basic requirement - what I am hoping to be able to do is further refine the basic Get-BrokerDesktop query to be able to count the number on machines output to say > 3 (ie more than 3 machines in MaintMode is unacceptable) and also check that the MachineName property is not equal to a particular value, ie the 3 test machine names in the environment which may be expected to be in MaintenanceMode.
Hope this makes sense, if not I'll try and elaborate. Any help much appreciated!!
One of the limitations of Orchestrator is that you can only pass strings across the data bus, and you need to pass an array of objects. You need to serialize the object array to a string. One way to do that is to convert the array to json, then use convertfrom-json when you get it back to get an object array to work with.
Don't have a SCORCH server handy to test with, so this isn't tested at all.
$vdims = "MyCitrixPowershellSDKServer"
function vdidesktopcheck
{
asnp Citrix*
$result = Get-BrokerDesktop -InMaintenanceMode $True -DesktopGroupName Unidesk
$result
}
$output = invoke-command -computer $vdims -scriptblock ${function:vdidesktopcheck} |
select machinename,RegistrationState | ConvertTo-Json
$Output