Powershell: How to reference the nth element - html

I would like to grab the 3rd "td" element in "filterTable". How is this done in a .NET environment like Powershell ? I have tried numerous ways, like so:
$_.getElementsByTagName("td")[3]
$_.getElementsByTagName("td[3]")
$_.getElementsByTagName("td:3")
$_.getElementsByTagName("td{3}")
$_.getElementsByTagName("td"){3}
However none of these seem to work. Is there a way to do this? Thanks for any help. Here is some context of my code:
$textValues = #()
$textValues = $data.ParsedHtml.getElementById("filterTable") | foreach{
$_.getElementsByTagName("td") | foreach{
$_ | Select InnerText
}
}

You can use the item() method on the element collection returned by getElementsByTagName().
Just supply an index (zero-based):
$filterTable = $data.ParsedHtml.getElementById("filterTable")
$3rdTD = $filterTable.getElementsByTagName("td").item(2)
Alternatively, use Select-Object -Index:
$filterTable.getElementsByTagName("td") |Select-Object -Index 2

Related

definition inside a property in invoke-restmethod (JSON Body)

I'm pretty stuck and can't find anything about it on the internet. I'm also not sure how to describe the thing i'm looking for, so maybe someone can help me.
I've got some code to create a ticket in TopDesk through API using invoke-restmethod in PS.
For the request field in TopDesk, I need some output stored in a variable, but if I want to use a variable in the PS command, I need to define the JSON body with the use of #{} | covertTo-JSON (found that somewhere on the internet).
Now this parameter I need to put through, has to have a definition. I need to give in in the value is a email or a name.
$json = #{
"callerLookup" = "{ email : email#domain.com }"
} | Convertto-JSON
Now the thing is, TopDesk doesn't see the "{ email : email#domain.com }" as a correct value.
Before, I just the following (which will work, but can't use variables):
$body = '{"email": "automation#rid-utrecht.nl"}'
I hope I described my problem cleary enough and hope that someone can help me.
Thanks in advance.
Kind regards,
Damian
For ConvertTo-Json to produce the serialized { "property" : "value" } syntax, you must pass it an object that has a property called property and an associated value equal to value. You can easily create this scenario with the [pscustomobject] accelerator.
$json = #{
callerLookup = [pscustomobject]#{email = 'email#domain.com'}
} | ConvertTo-Json

Select lots of known IDs from a big JSON document efficiently

I am trying to get some value from json via jq in bash. With small value it work nice but with big json it work too slow, like 1 value for each 2-3 second. Example of my code:
json=$(curl -s -A "some useragent" "url" )
pid=$(cat idlist.json | jq '.page_ids[]')
for id in $pid
do
echo $pagejson|jq -r '.page[]|select(.id=='$id')|.url'>>path.url
done
The "pid" is list of id that I type before running script. It may contain 700-1000 id. Example object of json
{
"page":[
{
"url":"some url",
"id":some numbers
},
{
"url":"some url",
"id":some numbers
}
]
}
Is there any way to speed up it? In javascript it work faster than it. Example of javascript:
//First sort object with order
var url="";
var sortedjson= ids.map(id => obj.find(page => page.id === id));
//Then collect url
for ( x=0 ; x < sortedjson.length;x++) {
url+=sortedjson[x].url
};
Should I sort json like in javascript for better performance? I don't tried it because don't know how.
Edit:
Replaced "pid" variable with json to use less code and for id in $(echo $pid) with for id in $pid.
But it still slow down if id list more than about 50
Calling jq once per id is always going to be slow. Don't do that -- call jq just once, and have it match against the full set.
You can accomplish that by passing the entire comma-separated list of ids into your one copy of jq, and letting jq itself do the work of splitting that string into individual items (and then putting them in a dictionary for fast access)
For example:
pid="24885,73648,38758,8377,747"
jq --arg pidListStr "$pid" '
($pidListStr | [split(",")[] | {(.): true}] | add) as $pidDict |
.page[] | select($pidDict[.id | tostring]) | .url
' <<<"$pagejson"
The following solution uses the same approach as the one posted by Charles Duffy (*) but is only applicable:
if each of the specified id values in $pid appears at most once as an id in the JSON objects in the .page array; or
if the goal is to extract, for each id in $pid, at most one corresponding object from the .page array.
The idea is to remove an id from the dictionary once it is found, and to stop if and when all ids have been found.
jq --arg pidListStr "$pid" '
($pidListStr | [splits(" *, *") | {(.): true}] | add) as $pidDict
| label $finish
| foreach .page[] as $page ($pidDict + {emit:null};
if length == 1 then break $finish
else ($page.id | tostring) as $id
| if .[$id] then delpaths([[$id]]) | .emit = $page.url
else .emit = null
end
end;
.emit // empty )
'
(*) Caveat
Using $pidDict here assumes there are no "collisions"; this condition would hold if all the id values in the .page objects are numeric.
The following is a response to the original question, which posited:
pid="24885,73648,38758,8377,747"
echo $pagejson|jq -r '.page[]|select(.id=='$pid')|.url'
(Based on subsequent edits to the question, it would appear that the intent was to iterate over the id values separately, invoking jq once per value. That is a bad idea as well but can be dealt with in a separate response.)
Response to original question
There are several problems with the invocation of jq based on
interpolating $pid as was originally done.
The major problem is that your query, when expanded, includes this select statement:
select(.id==24885,73648,38758,8377,747)
whereas what you evidently intend is:
select(.id==(24885,73648,38758,8377,747))
It's not difficult to see that there's a huge difference, which affects both functionality and performance.
Since you don't give any hints about the expected input, it's not feasible to suggest how the query might be optimized. To illustrate, though, suppose it's known that the .id values in the input are distinct. Then once all the ids in the query have been found, execution can stop.
In general, passing shell variables in by string interpolation is not a great idea. Some alternatives to consider are using --arg or --argjson.
The following solution, which is based on the one posted by Charles
Duffy (*), can be used if each of the specified id values in $pid
appears at most once as an id in the JSON objects in the .page array.
The idea is to stop if and when all the $pid ids have been found.
This can be accomplished with the following helper function:
def first_n(stream; $n):
label $done
| foreach stream as $x (-1; .+1; if . >= $n then break $done else $x end);
The solution can then be written as follows:
($pidListStr | [splits(" *, *") | {(.): true}] | add) as $pidDict
| ($pidDict|length) as $n
| first_n(.page[] | select($pidDict[.id | tostring]) | .url; $n)
This solution is similar to the one using foreach posted elsewhere
on this page, but is simpler and probably slightly more efficient as
the dictionary, once constructed, is unaltered.
The solution using foreach, however, can also be used if the ids of the
objects in the .page array are not unique, and if the goal is to
extract, for each id in $pid, at most one corresponding object from
the .page array.
(*) Caveat
Using $pidDict here assumes there are no "collisions"; this condition would hold if all the id values in the .page objects are numeric.

List Agent real names from Get-CsCallQueue cmdlet

I run the Get-CsCallQueue | Select-Object -Property Name,Agents cmdlet, but I want to see the real names of the agents. Instead I get something like hashes(?).
How can I see the names?
Get-CsCallQueue | Select-Object -Property Name,Agents
Name Agents
---- ------
CQ1 {adfe5681-ebc8-xxx-xxxx-........, OptIn}
CQ2 {adfe5681-ebc8-xxx-xxxx-......., OptIn}
CQ3 {baae77b8-5ace-xxx-xxxx-......, OptOut}
Is this Skype for Business Online (SFBO) or on-prem? You need to match up the agent GUID with the agent name. You'll need to use different cmdlets depending on your answer. Here's an example of how to do this using SFBO:
$queue = get-cscallqueue -NameFilter "<queue name here>"
$agents = $queue.agents
foreach ($agent in $agents) {
$user = $agent.ObjectId | Get-CsOnlineUser
$agent | Add-Member -NotePropertyName Name -NotePropertyValue $user.alias
}
$agents|Select Name,OptIn
Thanks,
Jason
Working on this with a Script that does the heavy lifting for you.
Module is Teamsfunctions on PSgallery.
The command is Get-TeamsCallQueue. I have surfaced all friendly names for Get/New/Set/Remove for CallQueues (I still need to finish testing on them, so handle with care :), should be finished in the coming weeks)
There is also Find-AzureAdUser in my module so that you can get the Object by feeding it the UPN instead of the ObjectID.
Hope that helps :)

Parse JSON in PowerShell SELECT

I'm trying to list all my Azure VMs with their sizes using a powershell command.
Problem is that the HardwareProfile property returns a JSON object, that I would like to parse and use only the vmSize property value of that object.
So I'm running this command:
Get-AzureRmVM
Which gives me this:
ResourceGroupName : TESTRG
...
Name : ubuntu-server
...
HardwareProfile : {
"vmSize": "Standard_DS2"
}
...
NOTE the JSON in the HarwareProfile value.
What I want to do is:
Get-AzureRmVM | Select ResourceGroupName, Name, HardwareProfileText `
| Out-Gridview -PassThru
Which works - only, I would like to get rid of the JSON notation in the HardwareProfileText. Using Format-Table looks like so:
ResourceGroupName Name HardwareProfileText
----------------- ---- -------------------
TESTRG ubuntu-server {...
So the question is: how can I get only the value of vmSize in this table ? Can I sneak ConvertFrom-Json in somewhere?
Can't you use the select-expression directly and convert the json-string into an object? So you can use it later in your pipeline.
Something like :
select #{Name="VMSize";Expression={($_|ConvertFrom-Json).vmSize}};
Given your json as a text in a file (for a simple-test):
(Get-Content -raw C:\tmp\test.json)|select #{Name="VMSize";Expression={($_|ConvertFrom-Json).vmSize}};
This will give you a property with only the VmSize. You can combine the expression select with normal properties as well, or multiple expressions and then continue to pass it down the pipeline if you want to filter on additional criteria.
I don't know the get-azureRmVm function but it works just fine with the property InstanceSize instead of HardwareProfileText.
Import-Module 'Azure'
Get-AzureVM | Select ResourceGroupName, Name, InstanceSize `
| Out-Gridview -PassThru
Result

Making script read a CSV and output answer

I am looking to add a line(s) into a PowerShell script. I want to get my script to check if something exists in a CSV it will give a true/false response.
Basically I have a script to remove user access from O365 and AD including and mailbox changes.
My company also uses a lot of external portals which need removing manually.
I would like it to check the $EmailAddress input against CSV's (i.e DomainHostAccess.CSV, WebsiteHostAccess.CSV, SupplierAccess.Csv) then if there name is in the list it will output something similar to;
DomainHostAccess | True
WebsiteHostAccess | False
SupplierAccess | True
that way we know that we need to manually log into these services and remove accounts.
I have looked on the posts on here already and couldn't find anything suitable, I am fairly new to PS and this is a little advance for me so I would appreciate any help that can be given.
You could try something like this
$UserList = Import-Csv -Path C:\EmailAddress.CSV
$UserData = Import-Csv -Path C:\DomainHostAccess.CSV
ForEach ($Email in $UserList)
{
$userMatch = $UserData | where {$_.Name -like $Email.Name}
If($userMatch)
{
Write-host $Email.Name "Domain Access True"
}
Else
{
Write-host $Email.Name "Domain Access False"
}
}
You would need to have headers on your CSVs for this check to work.