Powershell Strings from Objects - string

Trying to take an object and filter down to a value as a string. And save that string to variable and then use that variable in another command as a value for a flag.
So this command will get the PNPDevice InstanceID
$x = (Get-PnpDevice -PresentOnly -Class 'Net' | Where-Object {$_.FriendlyName -EQ 'Intel(R) Ethernet Connection I217-LM'} | Select-Object -ExpandProperty InstanceId | Format-Table -AutoSize | out-string )
Here I am checking the variable X and its content and type since the next command Disable-PNPDevice and the flag instanceID has to be a string
PS C:\Temp> $x.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
PS C:\Temp> echo $x
PCI\VEN_1234&DEV_543A&SUBSYS_32A39857&REV_04\3&1234839&0&C8
When I try to use $x for -InstanceId i get this error
PS C:\Temp> Disable-PnpDevice -InstanceId $x
Disable-PnpDevice : Invalid query
At line:1 char:1
+ Disable-PnpDevice -InstanceId $x
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (Win32_PnPEntity:ROOT\cimv2\Win32_PnPEntity) [Disable-PnpDevice], CimException
+ FullyQualifiedErrorId : HRESULT 0x80041017,Disable-PnpDevice
However if I manually create the variable with a string it works fine
PS C:\Temp> $y = "PCI\VEN_1234&DEV_543A&SUBSYS_32A39857&REV_04\3&1234839&0&C8"
PS C:\Temp> Disable-PnpDevice -InstanceId $y
PS C:\Temp> $y.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
Thank you.

Omit | Format-Table -AutoSize | out-string from your command, which is not only redundant in your case (the .InstanceId property already is a string), but causes the instance ID string to have a trailing newline, which is likely the cause of your problem.
Generally:
Format-* cmdlets emit output objects whose sole purpose is to provide formatting instructions to PowerShell's for-display output-formatting system. In short: only ever use Format-* cmdlets to format data for display, never for subsequent programmatic processing - see this answer for more information.
As an aside: That Out-String blindly appends a trailing newline to its output string is both unexpected and inconvenient: see GitHub issue #14444.
Note: With a single input object (such as in your case), -NoNewLine can be used to suppress the trailing newline (but Out-String is never needed for an input object that already is a string).
The problem is that with multiple input objects, -NoNewLine also suppresses newlines between their representations; e.g., 'one', 2 | Out-String -NoNewLine yields verbatim one2

Related

How to parse a here-string in PowerShell as a hash?

This example is easy enough:
PS C:\Users\saunders\Desktop\misc>
PS C:\Users\saunders\Desktop\misc> $hash = #"
>> Name = Jackson
>> Employer = BigData
>> EmpID = 2032
>> Type = Permanent
>> "#
PS C:\Users\saunders\Desktop\misc>
PS C:\Users\saunders\Desktop\misc> $hash
Name = Jackson
Employer = BigData
EmpID = 2032
Type = Permanent
PS C:\Users\saunders\Desktop\misc>
PS C:\Users\saunders\Desktop\misc> $hash | ConvertFrom-StringData
Name Value
---- -----
Employer BigData
Type Permanent
EmpID 2032
Name Jackson
PS C:\Users\saunders\Desktop\misc>
Which is excellent and the desired output. Here:
PS C:\Users\saunders\Desktop\misc>
PS C:\Users\saunders\Desktop\misc> cat .\hash.txt
$hash = #"
Name = Jackson
Employer = BigData
EmpID = 2032
Type = Permanent
"#
PS C:\Users\saunders\Desktop\misc>
PS C:\Users\saunders\Desktop\misc> $foo = Get-Content .\hash.txt
PS C:\Users\saunders\Desktop\misc>
PS C:\Users\saunders\Desktop\misc> $foo
$hash = #"
Name = Jackson
Employer = BigData
EmpID = 2032
Type = Permanent
"#
PS C:\Users\saunders\Desktop\misc>
the data originates in a file. How is this file read as a here-string so that it can be used as above?
Your .\hash.txt is in effect a .ps1 script file, so you could change its extension an then load it via ., the dot-sourcing operator, at which point $hash will be defined and can be passed to ConvertFrom-StringData.
To process the file as-is, you can pass its content to Invoke-Expression (though note that this cmdlet should generally be avoided):
Invoke-Expression (Get-Content -Raw t.txt)
$hash | ConvertFrom-StringData
If you have control over how .\hash.txt gets created and can change its name, you can save it as a PowerShell data file, i.e. with extension .psd1, which can be read as-is with the Import-PowerShellDataFile cmdlet:
A .psd1 file is in effect the source-code representation of a hashtable literal, whose syntax requirements are more stringent than for text that can be parsed with ConvertFrom-StringData:
# Save the source-code representation of a hashtable literal
# to a file - do NOT include a variable assignment:
#'
#{
Name = 'Jackson'
Employer = 'BigData'
EmpID = 2032
Type = 'Permanent'
}
'# > hash.psd1
Import-PowerShellDataFile hash.psd1
As an aside:
As of PowerShell 7.3.2, both ConvertFrom-StringData and Import-PowerShellData do not preserve the entries in definition order, given that [hasthable] instances are inherently unordered.
GitHub issue #19070 proposes overcoming this limitation, by making these cmdlets return ordered hashtables instead, via System.Management.Automation.OrderedHashtable, which derives from [hashtable].

Replacing specific strings in all matching files content with the file's basename using PowerShell

Get-ChildItem 'C:\Users\Zac\Downloads\script test\script test\*.txt' -Recurse | ForEach {(Get-Content $_ | ForEach { $_ -replace '1000', $fileNameOnly}) | Set-Content $_ }
I have been trying to use a simple PowerShell script to replace the 1000 value in my documents with the goal of replacing the value with the name of the .nc1/.txt file it is editing.
For example a file that is called BM3333.nc1 has a line value of 1000 which needs to replace it with BM3333 so on, so forth. This will be used in batch editing.
What is the variable that I use for replacing the 1000 with the file name?
So far, I can get this to run but it doesn't replace the 1000 value, it removes it.
Your problem is that inside the ScriptBlock of a ForEach-Object invocation, the variable is $_ (also known as $PSItem). There is no name for the inner script to get the value from the outer script.
You need to create a unique name in the outer script beforehand. The ScriptBlock argument to ForEach-Object does not need to be a single expression. You can either use multiple lines or a ;.
1..3 | ForEach-Object { $a = $_; 100..105 | ForEach-Object { $_ * $a } }
For your use case, you need this variable to be the name of the file. The values in the outer ScriptBlock are System.IO.FileSystemInfo, which were returned by Get-ChildInfo.
PowerShell makes iterating on work like this very easy; try seeing which properties are available:
Get-ChildItem 'C:\Users\Zac\Downloads\script test\script test\*.txt' -Recurse | Select-Object -First 1 | Format-List *

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 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 ';'

Adding data to variable results in a singleline string

I have a PowerShell PSCustomObject $result which I am filtering with multiple Where-Object statements.
$SatServers = $Global:result | Where-Object {$_ -like '*sat?2:00*' -and `
$_.MaintenanceWindow -notmatch 'all.da.servers' -and `
$_.Server -match "^IT"} | % {"{0}" -f $_.Server}
$SatServers += $Global:result | Where-Object {$_ -like '*sat?18:00*' -and `
$_.MaintenanceWindow -notmatch 'all.da.servers' -and `
$_.Server -match "^IT"} | % {"{0}" -f $_.Server}
$SatServers | Out-File d:\path\file.txt
If I output to the console or if I pipe to Out-File it looks great but when I send the output to a variable as seen above, I get output on a single line.
Is there something I'm missing in order to get a variable with a multiple line result? -Thanks!
You need to initialize $SatServers as an array first. You first call makes $SatServers a string-object and when you append (+=) a string to a string it simply adds it to the end of the last string.
String:
$string = "Hello"
$string += "Frode"
$string.GetType() | ft -AutoSize
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
$string
HelloFrode
Array:
$string = #()
$string += "Hello"
$string += "Frode"
$string.GetType() | ft -AutoSize
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
$string
Hello
Frode
You could also have added a NewLine at the end of the string by doing the following change in your Foreach-Object-scriptblock (but personally I prefer the array-solution).
% { ("{0}" -f $_.Server) + [environment]::NewLine }

Resources