select-object -expandproperty with null property - powershell

I have a collection of objects with properties like id, username, email, current_sign_in_at, identities. Where Identities property is an array of objects with two properties. This would be a json representation of an object:
{
"id": 45,
"name": "Emilio Roche",
"username": "EROCHE",
"state": "active",
"identities": [
{
"provider": "ldapmain",
"extern_uid": "cn=roche\\, emilio,ou=xxxxxxxxxx"
}
]
}
But some of the elements in the list do not have the identities property. So, when I do:
Get-Collection | Select-Object id, username -ExpandProperty identities
I get only those elements with identities property. I need all the entities, with or without identities property

If there are not too many properties to handle, you could use something like this:
Get-Collection | Select-Object id,
username,
#{n='provider';e={$_.identities.provider}},
#{n='extern_uid';e={$_.identities.extern_uid}}
This will return $null on the properties provider and extern_uid for those objects, that do not have the identities property:
id username provider extern_uid
-- -------- -------- ----------
45 EROCHE ldapmain cn=roche\, emilio,ou=xxxxxxxxxx
46 EROCHE
EDIT
As mklement0 pointed out, that approach doesn't work, if the identities property holds more than one object.
mklement0's answer has an elegant solution to this problem and should have been the accepted answer.

Note: This answer addresses the question as asked, but, judging by what answer was accepted, the real problem must have been different.
Select-Object -ExpandProperty identities id, username outputs an object for each individual identity in the identities array.
In order to include input objects that lack an identities property, you must therefore provide a placeholder dummy identity for them, which is what the following code demonstrates, using placeholder identity [pscustomobject] #{ provider='none'; extern_uid='none' } via an auxiliary Select-Object call that uses a calculated property to ensure the existence of an identities property.
# Sample JSON:
# * The 1st object has *2* identities,
# * the 2nd one none.
$json = '[
{
"id": 45,
"name": "Emilio Roche",
"username": "EROCHE",
"state": "active",
"identities": [
{
"provider": "ldapmain",
"extern_uid": "cn=roche\\, emilio,ou=xxxxxxxxxx"
},
{
"provider": "ad",
"extern_uid": "cn=roche\\, emilio,ou=yyyyyyyyyy"
}
]
},
{
"id": 46,
"name": "A. Non",
"username": "ANON",
"state": "dormant"
}
]'
($json | ConvertFrom-Json) |
Select-Object id, username, #{ n='identities'; e={
if ($_.identities) { $_.identities }
else { [pscustomobject] #{ provider='none'; extern_uid='none' } }
} } |
Select-Object id, username -ExpandProperty identities
The above yields:
provider extern_uid id username
-------- ---------- -- --------
ldapmain cn=roche\, emilio,ou=xxxxxxxxxx 45 EROCHE
ad cn=roche\, emilio,ou=yyyyyyyyyy 45 EROCHE
none none 46 ANON
Note how EROCHE is represented twice, once for each identity.

Related

How to filter a part of an output in powershell

I'm struggling with filtering the sessionId from the following output:
status messages data
------ -------- ----
Success {#{code=success; message=Success}} #{sessionId=0662c4d429bb51ef772e875f9c9b3a46; faSessionId=qfvio0382283ihbknhfh70kvn4; phpSessionId=qfvio0382283ihbknhfh70kvn4}
I've tried with the following:
$sessionId = $response | select "data"
This is returning:
data
----
#{sessionId=f77d5bfb5bdc65163e605d9c7edbcaed; faSessionId=hrsbd5iuq8i6dttfhimpm5baf1; phpSessionId=hrsbd5iuq8i6dttfhimpm5baf1}
I can't get it working to only extract the sessionId part.
It would be great if anyone could assist with the correct PowerShell code to obtain this.
It might help to think of the object you're working with like this, in JSON.
{
"Status": "Success",
"Messages": {
"code": "success",
"message": "Success"
},
"Data": {
"faSessionId": "qfvio0382283ihbknhfh70kvn4",
"phpSessionId": "qfvio0382283ihbknhfh70kvn4",
"sessionId": "0662c4d429bb51ef772e875f9c9b3a46"
}
}
You want the sessionId propeerty so you're selecting Data, but then you're finding that Data actually has those other properties of phpSessionId and faSessionId. Fortunately you have two approaches to get what you need.
Continue using the Select-Object cmdlet
You are already using this cmdlet but using the alias of Select, so you can keep using Select-Object to drill down all the way to the center of the Earth basically.
$response | Select-Object -ExpandProperty Data | Select-Object -ExpandProperty sessionId
>0662c4d429bb51ef772e875f9c9b3a46
Drill into the property you want with Dereferencing
This is the more programmery way to do it but is very popular. It's also called dot-notation.
$response.Data.sessionId
0662c4d429bb51ef772e875f9c9b3a46

ConverTo-Json altering intended output

I have a psobject that is created from the JSON output of an Invoke-RestMethod. My intention is to change one value, convert back to JSON, then add back to the application REST API with another Invoke-RestMethod. I have done this several times in the past with the same REST API so I'm not sure why this one isn't working.
The psobject $restOut looks like this:
id: 123
limit: #{limitMb=0; limitPercent=0}
The next block of code changes the id if the new id I want isn't already set
$newId = 456
if($restOut.id -ne $newId){
$restOut.id = $newId
$inputJson = $restOut | ConvertTo-Json -Depth 2
Invoke-RestMethod -Uri $restURl -Method PUT -Body $inputJson
}
I'm expecting $inputJson to look like this (and the psobject $restOut does match the expectation):
{
"id": "456",
"limit": {
"limitMb": 0,
"limitPercent": 0
}
}
But what I'm actually getting is:
{
"id": {
"value": "456",
"id": "456"
},
"limit": {
"limitMb": 0,
"limitPercent": 0
}
}
As said, I've done this exact manipulation many times in other scripts targeting the same software API, and am just at a loss with the behavior this time. Any help is appreciated. Thanks!
Easy fix
This was a simplified sample. In my actual script $newId = 456 was actually being assigned from another API call. Therefore it was also an object. Simply quoting it in the line that changes the id to make it a string fixed the issue:
$restOut.id = "$newId"
instead of
$restOut.id = $newId

REST: Single representation of responses for two REST requests that return object/objects of type 'A'

I have two REST URLs....
{host}/data/account
that returns a collection of accounts. We can model response for this request as shown below as a collection of accounts
{
"items": [{
"prop_name_1": "val_1",
"prop_name_2": "val_2",
|
|
|
|
"prop_name_n": "val_n",
"Links": [{child_links}]
}],
"Links": [{
{self_link},
{pagination_links}
}]
}
{host}/data/account/{particular_account_id}
that returns only a single account. We can model response for this request as only a single account as shown below
{
"prop_name_1": "val_1",
"prop_name_2": "val_2",
|
|
|
|
"prop_name_n": "val_n",
"Links": [{child_links}]
}
Now the question is, can I model responses for both these requests as only collection of accounts instead of two different representations?
The reason for doing it is the simplicity of parsing the response and also consistency in representation. Also, a collection can be used to represent a single object as well. Am I correct?

Retrieve UserName from ServiceNow

I am able to retrieve records for a particular Incident ID using Invoke-RestMethod. However, while retrieving the data, values like Resolved To, Updated By, etc. get populated by a sysid.
Resolved By comes in this format:
https<!>://devinstance.servicenow.com/api/sysid, value= sysid
I would like to view the username instead of the sysid.
The 'User ID' (user_name) isn't on the Incident, it's on the sys_user table, so you'll have to dot-walk to it.
If you're using the table API, you'll need to specify a dot-walked field to return, using the sysparm_fields query parameter.
This is no problem, just specify your endpoint like this:
$uri = "https://YOUR_INSTANCE.service-now.com/api/now/table/incident?sysparm_query=number%3DINC0000001&sysparm_fields=resolved_by.user_name"
I've specified a query for a specific incident number is requested, but you can replace that with whatever your query is.The important part is sysparm_fields=resolved_by.user_name. You'll want to specify any other fields you need here, as well.
The JSON I get as a result of running this API call, is the following:
{
"result": [
{
"resolved_by.user_name": "admin"
}
]
}
Note the element name: "resolved_by.user_name".
Another option for doing this, would be to tell the API to return both display, and actual values by specifying the sysparm_display_value parameter and setting it to all to return both sys_id and display value, or just true to return only display values.
Your URI would then look like this:
https://dev12567.service-now.com/api/now/table/incident?sysparm_query=resolved_byISNOTEMPTY%5Enumber%3DINC0000001&sysparm_display_value=all
And your JSON would contain the following:
"number": {
"display_value": "INC0000001",
"value": "INC0000001"
},
"resolved_by": {
"display_value": "System Administrator",
"link": "https://YOUR_INSTANCE.service-now.com/api/now/table/sys_user/6816f79cc0a8016401c5a33be04be441",
"value": "6816f79cc0a8016401c5a33be04be441"
},
"sys_updated_by": {
"display_value": "admin",
"value": "admin"
},
This would be accessed by:
answer.result[n].resolved_by.display_value

How to get FB group by alias (FQL)

Does there exist any way to get group by it's alias (username)?
SELECT id, name, pic_big, type, url, username FROM profile WHERE username='somealias'
works for pages, but doesn't works for groups! For groups it returns
(#803) Some of the aliases you requested do not exist: somealias
profile table is like a parent class for user, group, page, event, or application tables. (see https://developers.facebook.com/docs/reference/fql/profile/)
It has field username but values in this field are filled from related tables, and table group has no field username (see https://developers.facebook.com/docs/reference/fql/group). So when we query profile using where id = someid we get such results:
SELECT id, name, pic_big, type, url, username FROM profile WHERE id= '195466193802264'
{
"data": [
{
"id": 195466193802264,
"name": "Facebook Developers",
"pic_big": "https://www.facebook.com/images/litestand/bookmarks/sidebar/icons/groups/icon-default.png",
"type": "group",
"url": "https://www.facebook.com/groups/195466193802264/",
"username": ""
}
]
}
As you can see, username is empty for group.
update
After some investigation I found that groups CAN have usernames. Oficially they can't, but group's email part before # symbol is it's username.
When you query the profile if you only specify its username you receive nothing. But if you'll add its name your group will appear.
Example:
SELECT id, name, pic_big, type, url, username FROM profile
WHERE username = 'rock.mfc' and type = 'group'
will result
{
"data": [
]
}
But the query
SELECT id, name, pic_big, type, url, username FROM profile
WHERE name = 'ROCK MUSIC FANS CLUB' and username = 'rock.mfc' and type = 'group'
will give the required result:
{
"data": [
{
"id": 268045023346892,
"name": "ROCK MUSIC FANS CLUB",
"pic_big": "https://www.facebook.com/images/litestand/bookmarks/sidebar/icons/groups/icon-guitar.png",
"type": "group",
"url": "https://www.facebook.com/groups/rock.mfc/",
"username": "rock.mfc"
}
]
}
Hope it'll help you.