I've created a function "Query-ComDomElements.ps1" to query HTML objects.
This works quite well when querying only one object and querying that again.
When I try calling it in recursion it however fails and I don't understand why. The code/objects is/are the very same.
Could anyone please enlighten me why the query .container>img is not working, but querying .container and with that img is?
The error I get when querying both (and thus calling the function recursively) is:
Exception calling "InvokeMember" with "5" argument(s): "Unknown name. (Exception from HRESULT: 0x80020006 (DISP_E_UNKNOWNNAME))"
At C:\path\to\Query-ComDomElements.ps1:31 char:5
+ ... $result = [System.__ComObject].InvokeMember("getElementsB ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : COMException
Here my sample script (function Query-ComDomElements.ps1 not included but on github):
. C:\path\to\Query-ComDomElements.ps1
$ie = New-Object -ComObject "InternetExplorer.Application"
$ie.Navigate2("https://www.gpunktschmitz.de/")
while($ie.Busy) {
Start-Sleep -Seconds 1
}
#this works
$imgContainer = Query-ComDomElements -Query '.container' -Dom $ie.Document
$image = Query-ComDomElements -Query 'img' -Dom $imgContainer -Property 'src'
#this fails
$image = Query-ComDomElements -Query '.container>img' -Dom $ie.Document -Property 'src'
$ie.quit()
I think the problem is occurring because $dom ends up being an array with two elements when it is passed in on the second iteration. One (dirty) fix for this would be to use Select-Object to just get the first element (suggest using Select rather than [0] so that if its not an array it doesn't error):
if($SecondQuery -eq $false) {
if($Property -ne $false -and $Property -ne '*') {
return $result.$Property
} else {
return $result
}
} else {
return Query-ComDomElements -Query $SecondQuery -Dom ($result | select -first 1) -Property $Property
}
Related
We have a json file which contains an array with products with sell price and for some (the products we have to buy) a part number and buy price. For example:
{
"Prices":[
{
"Product":"AD User",
"Price":3.52
},
{
"Product":"Skype for Business Plus SAL",
"Price":4.68,
"Part number":"6SH-00002",
"Buy price":3.90
},
{
"Product":"Citrix CAL",
"Price":6.40,
"Part number":"ROYSPLACXABASE",
"Buy price":5.33
}
]
}
I use the following code to retrieve the contents of the json and for each object with a part number i put an object to an array of PSCustomObjects and add an int OrderCount and a function to increment this OrderCount:
$PurchaseOrder = #()
[object[]]$Prices = Get-Content 'C:\Pricelist.json' | Out-String | ConvertFrom-Json
foreach($_price in ($Prices.prices | Where-Object {$_."Part Number"})){
$PurchaseOrderItem = $_price.psobject.copy()
$PurchaseOrderItem | Add-Member -Name "OrderCount" -MemberType NoteProperty -Value 0
$PurchaseOrderItem | Add-Member -MemberType ScriptMethod -Name IncrementOrderCount {
param([int]$amount)
$this.OrderCount = $this.OrderCount + $amount
}
$PurchaseOrder += $PurchaseOrderItem
}
If figured out the following function to update the $PurchaseOrder:
function Update-PurchaseOrder{
[Cmdletbinding()]
param(
[ref]$PurchaseOrder,
[string]$ProductName,
[int]$Count
)
$PurchaseOrder.Value.where({$_.Product -eq $ProductName}).IncrementOrderCount($Count)
}
Instead I get the following error when I run this function
Update-PurchaseOrder -PurchaseOrder ([ref]$PurchaseOrder) -ProductName "Citrix CAL" -Count 5
Method invocation failed because [System.Collections.ObjectModel.Collection`1[[System.Management.Automation.PSObject, System.Management.Automation, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]] does not contain a
method named 'IncrementOrderCount'.
At line:9 char:5
+ $PurchaseOrder.Value.where({$_.Name -eq $ProductName}).IncrementO ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
If i run this code with a $Prices object which is created manually, for example:
$Prices = #([pscustomobject]#{Product="Product1";Price=1},[pscustomobject]#{Product="Product2";Price=2})
It does run without any problem. I've tried to cast several objects to different types, but I'm out of options.
I've added an additional piece of code to check if the Product exists in my PurchaseOrder, now it runs without problem.
Write-Verbose "Updating PurchaseOrder for product $Title "
if($PurchaseOrder.Value.where({$_.Product -eq $Title})){
try{
$PurchaseOrder.Value.where({$_.Product -eq $Title}).IncrementOrderCount($Count)
} catch {
Write-Warning "Error while incrementing OrderCount for product $($Title): $($_.Exception.Message)"
}
} else {
Write-Verbose "Product $Title not found in PurchaseOrder."
}
Use ForEach-Object to iterate over the collection and then call the method on each object.
Also the property with the Product name is Product rather than Name.
function Update-PurchaseOrder{
[Cmdletbinding()]
param(
[ref]$PurchaseOrder,
[string]$ProductName,
[int]$Count
)
$PurchaseOrder.Value.where({$_.Product -eq $ProductName}) | ForEach-Object {$_.IncrementOrderCount($Count)}
}
Also I believe $PurchaseOrder would already be passed by reference so you could skip the extra hassle and just pass $PurchaseOrder rather than ([ref]$PurchaseOrder). And then not need the extra step of unpacking the value in the function.
I am still new to Powershell and haven't been able to find anything on this. I am running a REST GET request to a URI which I know for a fact returns a 404 from the server since the resource is not found.
I would like to be able to run a conditional that checks if it's a 404 and skip over it for further processing if this is the case however when I assign the request to a variable, then calling on that later, it just gives me the contents of what my request was. I have never seen anything like this before in other languages...
My basic premise is the following. I first fetch all group names, then loop through that array of names, include the current one in a new URL and make an additional request for that specific group which looks for a SHIFT which will always have the same name. If the group doesn't have that shift by name I want to skip to the next group, otherwise alter some attributes of that newly found shift object.
Here is what my code looks like, as you can see it's not acting correctly
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$user = '******'
$pass = ConvertTo-SecureString '*******' -AsPlainText -Force
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $user, $pass
$req = Invoke-WebRequest -Credential $cred -Uri https://********-np.xmatters.com/api/xm/1/groups
$res = ConvertFrom-Json $req.Content
$content = $res.data
$base = "https://********-np.xmatters.com/api/xm/1/groups"
$group_name = $content[0].targetName
$path = "$base/$group_name/shifts/MAX-Default Shift"
$shift = Invoke-RestMethod -Credential $cred -Uri $path
Write-Host '-----------------------'
Write-Host $shift
Write-Host '-----------------------'
... RESPONSE BELOW ....
Invoke-RestMethod : The remote server returned an error: (404) Not Found.
At \\MMFILE\********$\MyDocuments\Group Supers PReliminary.ps1:16 char:10
+ $shift = Invoke-RestMethod -Credential $cred -Uri $path
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
-----------------------
#{id=***********; group=; name=MAX-Default Shift; description="; start=2018-08-21T04:00:00.000Z; end=2018-08-22T04:00:00.000Z; timezone=America/New_York; recurrence=;
links=}
-----------------------
PS C:\WINDOWS\system32>
What I would like to do is something like, in shorthand code, if $shift.code == 404 ... skip ... else ... run additional query
You need to use a try ... catch.
$code = ""
try{
$shift = Invoke-RestMethod -Credential $cred -Uri $path
}
catch{
$code = $_.Exception.Response.StatusCode.value__
}
if($code -eq "404")
{
continue
# Other things
}
else
{
Write-Host '-----------------------'
Write-Host $shift
Write-Host '-----------------------'
}
You could suppress the error message via Try..Catch and in so doing allow the script to continue:
Try {
$Shift = Invoke-RestMethod http://www.google.com/fakeurl -ErrorAction Stop
#Do other things here if the URL exists..
} Catch {
if ($_.Exception -eq 'The remote server returned an error: (404) Not Found.') {
#Do other things here that you want to happen if the URL does not exist..
}
}
Note this will hide all terminating errors from Invoke-ResetMethod. You could then use an if statement to see if the exception was a 404 and then perform further actions accordingly.
I am trying to access Jtrac webpage using powershell. i am able to login into but i am unable to access SEARCH button which href link.
$Url = “http://kbserver/workflow/app/login”
enter code here`$Username=”XXXXX”
enter code here`$Password=”XXXXX”
$IE = New-Object -com internetexplorer.application;
$IE.visible = $true;
$IE.navigate($Url);
while ($IE.Busy -eq $true)
{
Start-Sleep -Milliseconds 2000;
}
$Login = $IE.document.getElementById("loginName3").value = "$Username"
$Login = $IE.Document.getElementById(“password12”).value= "$Password"
$Login = $IE.Document.getElementsByTagName("input") | where-object {$_.type -eq "submit"}
$Login.click();
while ($IE.Busy -eq $true)
{
Start-Sleep -Milliseconds 5000;
}
$Login = $IE.Document.getElementsByTagName("a") | where {$_.href -eq "'?wicket:interface=:2:table:dashboardRows:3:dashboardRow:search::ILinkListen er::'"}
$Login.click();
Error which i am getting is
You cannot call a method on a null-valued expression.
At C:\Users\Dinesh\Webbb.ps1:20 char:13
+ $Login.click <<<< ();
+ CategoryInfo : InvalidOperation: (click:String) [], RuntimeException
+ FullyQualifiedErrorId : InvokeMethodOnNull
<a href="?wicket:interface=:2:table:dashboardRows:3:dashboardRow:search::ILinkListener::">
<img title="SEARCH" src="../resources/search.gif"> </a>
It looks like you have a typo in line 19, ILinkListen er:: at the end of your where instead of ILinkListener::. Where isn't finding anything to match, so it returns null, and that's how you get your null-valued $Login object.
Edit: Your problem is still line 19, you should print out $IE.Document.getElementsByTagName("a") and verify that it's actually returning what you expect, because your where filter is not matching anything and returning $null to $Login.
Edit2: went to a website and grabbed all the hrefs and took a look, powershell removes the "" from the hrefs.
you should have $IE.Document.getElementsByTagName("a") | where {$_.href -eq "?wicket:interface=:2:table:dashboardRows:3:dashboardRow:search::ILinkListener::"} instead.
Edit 3: Your second issue can be solved with regex using the -match operator instead of -eq (-eq does not really work if the item you are checking is dyanmic ).
Like this:
$IE.Document.getElementsByTagName("a") | where {$_.href -match "\?wicket:interface=:\d"}
This regex will return anything that containts "?wicket:interface=:digit".
One digit is the best option here since it will keep growing on refresh, assuming nothing else on the page matches that this should be good.
I want to add the profile pictures of the exchange to our users in Active Directory.
I tried to do this in Powershell, but cause I'm pretty new to Powershell i'm already stucking.
My Script:
$Name = #()
$Photo = #()
Import-Csv C:\temp\adusers.csv |
ForEach-Object {
$Name += $_.DisplayName
}
Import-Csv C:\temp\photolinks.csv |
ForEach-Object {
$Photo += $_.PSChildName
}
$Name | ForEach-Object {
Where $Photo -Like "*$Name*" {
Set-UserPhoto "$Name" -PictureData ([System.IO.File]::ReadAllBytes("F:\1 path\" + $Photo))
}
}
I created a ps script to get all our AD Users before and I created a list of our userphotos with gci before as well. I exported those results in a csv and I now want to import thos data and tell Powershell add for every User in the List adusers a photo with Set-UserPhoto.
The error message I got is the following:
Where-Object : A positional parameter cannot be found that accepts argument 'System.Object[]'.
At C:\Users\user1\Desktop\setuserphoto.ps1:14 char:13
+ Where $Photo -Like "*$Name*" {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Where-Object], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WhereObjectCommand
Can anyone help me here?
This is my code, it's very simple:
$TargetFolder = 'M:\'
try{
$Getfolderlist = Get-ChildItem $TargetFolder -Recurse | ? { $_.PSIsContainer -and $_.Name -eq 'old' }
}catch {
Write-Host "Error ! :) "
}
It doesn't work and I get an powershell exception:
Get-ChildItem : Cannot find drive. A drive with the name 'M' does not exist.
At C:\ef-scripts\PurgeDeliveryZip\purge_delivery_zip.ps1:23 char:18
+ $Getfolderlist = Get-ChildItem $TargetFolder -Recurse | ? { $_.PSIsContainer -an ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (M:String) [Get-ChildItem], DriveNotFoundException
+ FullyQualifiedErrorId : DriveNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
Thanks for your help.
You need -ErrorAction set to Stop. This way the catch block gets the exception. Read about try catch usage here: http://technet.microsoft.com/en-us/library/hh847793.aspx
Also, you may want to read about terminating errors in PowerShell and ErrorAction common parameter helps.
$TargetFolder = 'M:\'
try{
$Getfolderlist = Get-ChildItem $TargetFolder -Recurse -Ea Stop | ? { $_.PSIsContainer -and $_.Name -eq 'old' }
}
catch {
Write-Host "Error ! :) "
}