Search for a unknown property in an object based on its value - string

I am not sure how to do this with the usual suspects, namely Where-Object or Select-Object.
Suppose I want to find the string "needle" in PSCustomObject $Object, and that object can have several Note properties, such as $Object.Haystack1, $Object.Haystack2 and so on ..
In my case the number of note properties is known and fixed, but I'd like to know what to do for the harder case when you don't know how many properties your object has.
I poked around with Select/Where-Object and the operator -in but hadn't managed to make an easy, elegant one liner that does the job.

$obj = [pscustomobject]#{'Haystack1'='test';'Haystack2'='needle'}
$noteProperties = $obj|get-member -MemberType NoteProperty | select -ExpandProperty name
$noteProperties | Where {$obj."$_" -match 'needle'}
and you can one-liner it with
$obj|gm -M NoteProperty|?{$obj."$($_.Name)"-match'needle'}

One possibility:
$obj = [pscustomobject]#{'Haystack1'='test';'Haystack2'='needle'}
#($obj | Format-List *| Out-String).split("`n") -like '*needle*'
Haystack2 : needle

$obj = [PSCustomObject]#{"Haystack1" = "test"; "Haystack2" = "needle"}
$obj.PSObject.Properties | ? { $_.Value -eq "needle" }

Related

Powershell odd behaviour when outputting to csv

I'm having a problem when outputting my foreach loop to a csv file.
My Groups are set like this:
$Groups = "Group1", "Group2", "Group3"
My code is:
$results = ForEach ($Group in $Groups) {
$memberof = get-adgroup $Group | select -expandproperty distinguishedname
Write-Output $Group
Get-ADObject -Filter 'memberof -eq $memberof -and (ObjectClass -eq "user" -or ObjectClass -eq "contact")' -properties * | select name, Objectclass, mail
Write-Output ""
Write-Output ""
}
$results | Export-csv Contacts.csv -NoTypeInformation
The problem seems to be coming from the Write-Output lines but I have no clue why. When I run my code without writing to a csv file, I get the expected result, something like:
NameOfGroup1
name Objectclass mail
---- ----------- ----
User1 user User1#mail.com
User2 user User2#mail.com
#Spaces caused by write-output ""
NameOfGroup2
User1 user User1#mail.com
Contact1 contact Contact1#externalmail.com
Then again when I run my code to write to csv file and have the write-output $Group commented out I get a similar result.
But if I run my full code from the top of this page including the write-output $Group, it comes out like this:
I've figured out what these results represent but I haven't got clue why they do print out like this.
Eseentially the numbers refer to the length of the group name, so the first 17 would be a 17 character group name, and then the number of lines below is equal to the number of contacts and users that are inside that group. The 2 zeros at the end of each group are the length of the write-output "" lines.
What is causing this behavior?
The following code will closely output what you are attempting.
$results = ForEach ($Group in $Groups) {
$memberof = get-adgroup $Group | select -expandproperty distinguishedname
Get-ADUser -Filter "memberof -eq '$memberof' -and (ObjectClass -eq 'user' -or ObjectClass -eq 'contact')" -properties name,ObjectClass,Mail | Select-Object #{n='Group';e={$Group}},name, Objectclass, mail
[pscustomobject]"" | Select-Object Group,Name,ObjectClass,Mail
[pscustomobject]"" | Select-Object Group,Name,ObjectClass,Mail
}
$results | Export-csv Contacts.csv -NoTypeInformation
Explanation:
Export-Csv converts an object or array of objects with properties into a CSV file. You can see the same result in the console with ConvertTo-Csv. Properties are converted into columns and property values are placed under their associated columns. When you output a string as in Write-Output $Group, it has a property of Length. To fix this, you need to add $Group as a calculated property in your Select-Object. If you want to do blank lines in your CSV, then you should output another object with all of the property values as ''.
When you mix objects in your PowerShell outputs, you can see unexpected results. Your Get-ADObject outputs a custom object. Your Write-Output lines output a string. Those two object types do not share properties. So you only see the properties for the first object in your array, which is a string. If you put all of the Write-Output statements at the end of your loop, you will see more properties in your CSV. See below for an example that just by reversing the order of processed objects, you get a different result.
$str = "string"
$obj = [pscustomobject]#{property1 = "value1"; property2 = "value2"}
$str,$obj | convertto-csv -notype
"Length"
"6"
$obj,$str | convertto-csv -notype
"property1","property2"
"value1","value2"
,
Notice the properties available to the custom object $obj and the string $str.
$obj | get-member -Type Properties
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
property1 NoteProperty string property1=value1
property2 NoteProperty string property2=value2
$str | get-member -Type Properties
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Length Property int Length {get;}

How do I split/parse a URL string into an object?

I'm trying to split a URL into an object.
$url = "https://api.somedomain.com/v2/direct-access/producing-entities-details?entity_id=104194825&format=json&page=1
The desired result would be
PS C:\WINDOWS\system32> $url.api
https://api.somedomain.com/v2/direct-access
PS C:\WINDOWS\system32> $url.dataset
producing-entities-details
PS C:\WINDOWS\system32> $url.params
entity_id=104194825&format=json&page=1
I'm sure this can be done in combination with regex, but I am also wondering if this can be done just using built in PowerShell functionality.
No need to mess around with regex. The [uri] type accelerator would do some of that work for you. The other parts seem specific to how you choose to interpret the data and not how URL anatomy works.
PS C:\Users\matt> $url = [uri]"https://api.somedomain.com/v2/direct-access/producing-entities-details?entity_id=104194825&format=json&page=1"
PS C:\Users\matt> $url.Query
?entity_id=104194825&format=json&page=1
You can explore the other properties and see how they will help you. For instance, you might need to build the Segments to get the other parts you are looking for.
PS C:\Users\matt> $url.Segments
/
v2/
direct-access/
producing-entities-details
$url = "https://api.somedomain.com/v2/direct-access/producing-entities-details?entity_id=104194825&format=json&page=1"
$uri = [System.Uri]$url
$ParsedQueryString = [System.Web.HttpUtility]::ParseQueryString($uri.Query)
$i = 0
$queryParams = #()
foreach($QueryStringObject in $ParsedQueryString){
$queryObject = New-Object -TypeName psobject
$queryObject | Add-Member -MemberType NoteProperty -Name Query -Value $QueryStringObject
$queryObject | Add-Member -MemberType NoteProperty -Name Value -Value $ParsedQueryString[$i]
$queryParams += $queryObject
$i++
}
$queryParams
Output:
Query Value
----- -----
entity_id 104194825
format json
page 1

How to export value output without '#{}' after string replacement?

I'm trying to export forward list from Exchange 365 and remove "smtp:" string from output.
Here is PS code for Exchange 365 forwards list:
$forwards = Get-Mailbox –ResultSize Unlimited |
where {($_.ForwardingSmtpAddress –ne $null)} |
select identity, *forward*
$result = $forwards -replace "smtp:",""
$result
My $forwards value has format:
PS> $forwards
Identity DeliverToMailboxAndForward ForwardingAddress ForwardingSmtpAddress
-------- -------------------------- ----------------- ---------------------
name.surname1 True smtp:name.surname1#domain.com
name.surname2 True smtp:name.surname2#domain.com
name.surname3 True smtp:name.surname3#domain.com
I managed to remove that smtp: text with command:
$result = $forwards -replace "smtp:",""
But I get such ugly output for that variable:
PS> $result
#{Identity=name.surname1; DeliverToMailboxAndForward=True; ForwardingAddress=; ForwardingSmtpAddress=name.surname1#domain.com}
#{Identity=name.surname2; DeliverToMailboxAndForward=True; ForwardingAddress=; ForwardingSmtpAddress=name.surname2#domain.com}
#{Identity=name.surname3; DeliverToMailboxAndForward=True; ForwardingAddress=; ForwardingSmtpAddress=name.surname3#domain.com}
How to get rid of that #{} and present it right way as it was in $forwards variable? Maybe there is better way to remove smtp: from first value $forwards?
You should also be able to use calculated properties:
$forwards = Get-Mailbox –resultSize unlimited |
where { $_.ForwardingSmtpAddress –ne $null } |
select #{Name="Identity";Expression={$_.Identity}},
DeliverToMailboxAndForward, ForwardingAddress,
#{Name="ForwardingSmtpAddress";Expression={$_.ForwardingSmtpAddress -replace "smtp:",""}}
I don't have the means to test this right now, though.
Instead of creating a new variable it might be easier to modify the existing objects in your array. You need to do the replacement on each ForwardingSmtpAddress property:
$forwards | Foreach-Object {
$_.ForwardingSmtpAddress = $_.ForwardingSmtpAddress -replace "smtp:", ""
}
After this, just echoing $forwards to the screen should show you the updated values.
What would be the result when you use this snippet?
$forwards = Get-Mailbox –resultSize unlimited |
where {$_.ForwardingSmtpAddress –ne $null} |
select Identity, DeliverToMailboxAndForward, ForwardingAddress,
#{Name='ForwardingSmtpAddress';Expression={$_.ForwardingSmtpAddress.ToString().Replace('smtp:', $null)}}
If you don't wan't the #{} maybe you could convert or export it to a CSV format.
Export-Csv -Delimiter ';'
ConvertTo-Csv -Delimiter ';'

String manipulation on each noteproperty value

tried many different methods of trying to get this working. The is the closest non-working example i can come up with.
I want to get rid of NoteProperty items with a null value, if i also want to get rid of $ and ; characters from any NoteProperty value in an object while leaving the rest of the value behind could someone please advise me what is wrong with the following code example?
$JournalObject | Get-Member -MemberType NoteProperty | ForEach-Object {
if ($JournalObject.$_.Value -like ';')
{
$JournalObject.$_.Value.Replace(';', '')
}
if ($JournalObject.$_.Value -like '$')
{
$JournalObject.$_.Value.Replace('$', '')
}
if ($JournalObject.$_.Value -eq $null)
{
$JournalObject.PSObject.Properties.Remove($_)
}
}
Kindest regards !!
hoping for your help :)
Something to also note; if you are running a replace that's getting it's object from Get-Member, you aren't touching the original object to begin with.
You can see this for yourself by running another Get-Member where your ForEach-Object is to see what's being passed through the pipeline (spoiler, it's Microsoft.PowerShell.Commands.MemberDefinition)
You can also see this a little better by running it against a string with 1 Get-Member piped then comparing it with a second Get-Member piped:
"asfdasf" | Get-Member (this will return the expected String type)
VS
"asfdasf" | Get-Member | Get-Member (this will comeback as a MemberDefinition object, since you're literally getting the members of the Get-Member result)
Working on a better approach, will update this answer shortly.
This is what I came up with. First you need to prune out the the properties you aren't returning so that you aren't loop in on members that will no longer exist, by finding those members then excluding with select-object.
Then you can loop through and fix the values.
$members = $JournalObject | Get-Member -MemberType NoteProperty | select -expandproperty name
$removelist = $JournalObject | % {
foreach ($member in $members) {
if ($_.$member -eq $null) {
$member
}
}
}
$uremovelist = $removelist | select -unique
$prunedJournalObject = $JournalObject | select * -ExcludeProperty $uremovelist
$members = $prunedJournalObject | Get-Member -MemberType NoteProperty | select -expandproperty
$prunedJournalObject | % {
foreach ($member in $members) {
if ($_.$member -match ';') {
$_.$member = $_.$member.Replace(';', '')
}
if ($_.$member -match '$') {
$_.$member = $_.$member.Replace('$', '')
}
}
$_
}

Parse an object and add a member property to each member

I have an object which looks for example like this
Name Number
---- ------
John one
Major two
Mars one
I want to go through each member and check on Number and add a property that in the end it looks like this.
Name Number IsItOne
---- ------ -------
John one True
Major two False
Mars one True
What I got so far is do a foreach loop through the object but then I have two objects and have no chance as far as I know to change the original object.
Just another (shorter) version:
$obj | add-member -type scriptproperty -name IsItOne -value {$this.Number -eq 'one'} -passthru
It seems to be like you are talking about a set of objects with properties Name and Number.
If so, you can do like this:
$a | %{ $isitone = $false; if($_.Number -eq "one") {$isitone=$true} $_ |
add-member -Type noteproperty -name IsItOne -value $isitone }
Here is a possible alternative.
function new-stuff ($name, $number) {
New-Object PSObject -Property #{name=$name; number=$number}
}
$(
new-stuff John one
new-stuff Major two
new-stuff Mars one
) | ForEach { $_ | Add-Member -PassThru ScriptProperty IsItOne {$this.Number-eq"one"} }

Resources