Concatenate and aggregate multiple strings together in Powershell - powershell

I'm trying to Run Advertised Programs using PowerShell
$tpObject = Get-WmiObject -Namespace ROOT\ccm\Policy\Machine\ActualConfig -Class CCM_SoftwareDistribution `
| Select-Object -Property PKG_Manufacturer, PKG_Name, PKG_MIFVersion
The output will be:
PKG_Manufacturer PKG_Name PKG_MIFVersion
---------------- -------- --------------
Microsoft Word v1234
Google Chrome v987
Microsoft Excel v987
etc
How do I concatenate it into a string? I tried this:
[string[]]$result = $tpObject.PKG_Manufacturer + $tpObject.PKG_Name + " - " + $tpObject.PKG_MIFVersion
$result
But it display all the PKG_Manufacturer, then PKG_Name, then PKG_MIFVersion
I would like it to display this, Microsoft Word - v1234 as a string?
Any suggestions or comments would be greatly appreciated.
tks

Give this a try:
$result=#()
Get-WmiObject -Namespace ROOT\ccm\Policy\Machine\ActualConfig -Class CCM_SoftwareDistribution | %{
$result += "$($_.PKG_Manufacturer) $($_.PKG_Name) - $($_.PKG_MIFVersion)"}
$result

I guess what you want is something like this
$list = New-Object 'System.Collections.Generic.List[string]'
Get-WmiObject -Namespace ROOT\ccm\Policy\Machine\ActualConfig -Class CCM_SoftwareDistribution `
| ForEach-Object $list.Add("$($_.PKG_Manufacturer) $($_.PKG_Name) - $($_.PKG_MIFVersion)")
To concatenate and aggregate the values in a string array you have several options.
First, you can concatenate a string:
You can concat strings in PowerShell by using the + operator (as applied by you)
You can use the -f formatting approach (like in C#) "My PC {0} has {1} MB of memory." -f "L001", "4096"
Or you can use a double quoted string with variables $x = "Max"; Write-Output "I'm $x" (Hind: Sometimes you need sub-expressions expansion, as shown in my example above. See the Windows PowerShell Language Specification Version 3.0, p34).
Second, you can aggregate your results:
Using a dynamically-typed array $testArray = #() with $testArray += $_
Using a typed array [string[]]$testArray = [string[]] also with += as shown below.
Using a generic List $list = New-Object 'System.Collections.Generic.List[string]' with the Add-Method $list.Add($_)
And there is more...
In general, I would try to stay with the PS Objects as long as possible.
Aggregating stuff in a string array to process it later is too much (C#) programmer thinking.
In your Code:
1. You cannot add a new array element with the = operator, use += instead:
$testArray = #()
$tempArray = "123", "321", "453"
$tempArray | ForEach {$testArray += $item }
$testArray

$tpObject = Get-WmiObject -Namespace ROOT\ccm\Policy\Machine\ActualConfig -Class CCM_SoftwareDistribution `
$tpobject | ForEach-Object{
"{0} {1} - {2}" -f $_.PKG_Manufacturer, $_.PKG_Name, $_.PKG_MIFVersion
}
See details of the -f format operator
Sample output:
Microsoft Word - v1234
Google Chrome - v987
Microsoft Excel - v987

Related

Powershell - how to replace OS Version number with String

I am querying remote servers for their operating system. I know that I can return the Version, but I want to replace these values with the friendly name. The code I have so far is:
$Computer = (gc c:\servers.txt)
$BuildVersion = Get-WmiObject -Class Win32_OperatingSystem -Property Version, CSName -ComputerName $Computer -ErrorAction SilentlyContinue
$Build=$BuildVersion.version
If ({$BuildVersion.Version -match "5.2.3790"})
{$Build="2003"}
Elseif ({$BuildVersion.Version -match "6.1.7601"})
{$Build="2008"}
Elseif ({$BuildVersion.Version -like "6.3.9600"})
{$Build="2012"}
But this doesn't seem to work and only returns "2003" regardless. Please help, I'm fairly new to PS and coding.
thanks
The problem is your if statements. Putting the Boolean expression inside squiggly brackets makes it a script block, and that's going to get cast as a string before being cast as a Boolean. Strings cast to Booleans always evaluate to true unless they're empty.
PS C:\> {$BuildVersion.Version -match "5.2.3790"}
$BuildVersion.Version -match "5.2.3790"
PS C:\> ({$BuildVersion.Version -match "5.2.3790"}) -as [bool]
True
PS C:\> $BuildVersion.Version -match "5.2.3790"
False
PS C:\> ($BuildVersion.Version -match "5.2.3790") -as [bool]
False
So what you're running is essentially:
if ([bool]'$BuildVersion.Version -match "5.2.3790"') [...]
And that's always going to be true.
Try:
$Computer = (gc c:\servers.txt)
$BuildVersion = Get-WmiObject -Class Win32_OperatingSystem -Property Version, CSName -ComputerName $Computer -ErrorAction SilentlyContinue
$Build=$BuildVersion.version
If ($BuildVersion.Version -match "5.2.3790")
{
$Build = "2003"
}
Elseif ($BuildVersion.Version -match "6.1.7601")
{
$Build = "2008"
}
Elseif ($BuildVersion.Version -like "6.3.9600")
{
$Build = "2012"
}
Bottom line is that squiggly brackets are not parentheses and you can't use them like they are.
However, there's also a major logic error here. You're potentially fetching an array for $BuildVersion because you're reading from a file, but then you treat it like a single value. You never loop through $BuildVersion. However, I do not have enough information about what you're actually trying to do with your script (like what you do with $Build) to be able to fix that.
I originally said this, but I've since changed my mind
The reason this is only returning 2003 is that you're only running your If code on a single entry in the list.
Wrong
As TessellatingHeckler says, the reason your if wasn't working is that you had too many curly braces, so PowerShell wasn't actually evaluating your logic.
However, you still need to step through each of the computers to do what you're trying to do. We'll do that by adding in a ForEach loop. I also went ahead and replaced your If {} logic with a Switch statement, which I think is easier to understand for a scenario like this with multiple clauses. If's just get way too verbose.
Finally, I'm assuming you want to output the results too, so I added a custom object here, which is just a way of choosing which properties we want to display.
$Computer = (gc c:\servers.txt)
ForEach ($system in $computer){
$BuildVersion = Get-WmiObject -Class Win32_OperatingSystem -Property Version, CSName -ComputerName $system -ErrorAction SilentlyContinue
$Build=$BuildVersion.version
switch ($build){
"5.2.3790" {$Build="2003"}
"6.1.7601" {$Build="2008"}
"6.3.9600" {$Build="2012"}
}
#output results
[pscustomobject]#{Server=$system;OSVersion=$build;CSName=$buildVersion.CSname}
}#EndOfForEach
Output
>Server OSVersion CSName
------ --------- ------
dc2012 2012 DC2012
sccm1511 2012 SCCM1511
You can use this:
Get-WmiObject -Class Win32_OperatingSystem | Select-Object -ExpandProperty Caption
Additionally you can see everything this WMI object holds like this:
Get-WmiObject -Class Win32_OperatingSystem | fl *
Edit: if you want to remove some text from the string, you can use -replace:
(Get-WmiObject -Class Win32_OperatingSystem |
Select-Object -ExpandProperty Caption) -replace "Microsoft Windows Server ",""

Display test-connection successes and failures in Out-Gridview

I am trying to get a list of servers and the last time they rebooted to show in a table. However, if it doesn't respond to a ping, I just need it to show in the list. I can't seem to figure out how to get it to add to the table after else.
Import-CSV $Downtime | % {
if(Test-Connection $_.server -Quiet -count 1){
Get-WmiObject Win32_OperatingSystem -ComputerName $_.server |
select #{LABEL="Name"; EXPRESSION = {$_.PSComputerName}}, #{LABEL="Last Bootup"; EXPRESSION = {$_.convertToDateTime($_.LastBootupTime)}}
}
else{#{LABEL="Name"; EXPRESSION = {$_.server}}
}
} | Out-GridView
I can always save the else results in a text file but this would be more convenient.
You need to make the same object, with the same properties!, in both cases so that PowerShell will understand the association between the two. The follwing example builds a custom hashtable using the if/else and outputs the object for each loop pass.
Import-CSV $Downtime | ForEach-Object {
$props = #{}
$server = $_.server
if(Test-Connection $server -Quiet -count 1){
$wmi= Get-WmiObject Win32_OperatingSystem -ComputerName $server
$props.Name = $wmi.PSComputerName
$props."Last Bootup" = $wmi.convertToDateTime($wmi.LastBootupTime)
}else{
$props.Name = $server
$props."Last Bootup" = "Could not contact"
}
New-Object -TypeName psobject -Property $props
} | Out-GridView
I used $server as the $_ changes context a couple of time so we wanted to be able to refer to the current row in the CSV we are processing.
I don't know what your PowerShell version is so I will assume 2.0 and create objects that support that.
In both cases an object is created with a Name and Last Bootup property which is populated based on the success of the ping.
As an aside I had a similar question a while ago about created similar object based output.

-join operator on a variable for a parameter

function Get-Diskinfo {
param(
[string[]] $Computername = 'XEUTS001',
[string[]] $drive = 'c:'
)
$a = "-join $Computername[1..3]"
Get-WmiObject Win32_LogicalDisk `
-Filter "DeviceID = '$drive'" `
-ComputerName $Computername `
-Credential (Get-Credential -Credential ayan-$a) |
Select-Object `
#{n='Size'; e={$_.size / 1gb -as [int]}},
#{n='free';e={$_.freespace / 1gb -as [int]}},
#{n='% free';e={$_.freespace / $_.size *100 -as [int]}} |
Format-Table -AutoSize
}
I wrote this function to get some details about specific disks. However, I have to run them remotely and in a multi-domain environment. We have different usernames for computers in different OU's. I wanted the script to be able to take the username from the computername itself. The usernames are in this format ---- "name"+ "first 3 letters of the computername" which is the OU name. I am able to get the -Join method to work normally. However, it doesn't work if the variable is a parameter in a function. Here the username shows up as "ayan--join xeuts001[1..3]" when I want it to show up as "ayan-xeu"
What you have there is just a string that happens to contain a variable (which is expanded). Inside a string you are not in expression mode, so you cannot use operators. They just get embedded string content like you see there. What you want is probably:
$a = -join $Computername[1..3]
But that isn't correct, as it will yield oob for a computer name Foobar. If you want the first three letters, you'd need
$a = -join $Computername[0..2]
or even simpler (and easier to read, and faster):
$a = $Computername.Substring(0, 3)
P.S.: I also took the liberty of reformatting your original code, it was a horrible mess to read.

Powershell -- Exporting to CSV based on Selected Objects from an array

Sorry for the wierd title, I didn't know how to phrase the question. I'm relatively new to Powershell and I'm writing a program. Basically, I have an array where the user has selected settings or wish to have "Selected" from the GWMI query stored in "$settings_array". I want to output the results to a CSV. When I try to run it, only the first Select statement gets output to the CSV. Output to the textbox works fine. I know it has something to do with how it's being stored in the array at each ieration. $resultList is intialized as an array ($resultList = #()). There are hundreds of lines of code for the Form and other functions, but here is the relevant code. Thanks for the help! Let me know if I need to post more of the code.
$colItems = GWMI Win32_NetworkAdapterConfiguration -computername $PCname -namespace "root\CimV2" -filter "IpEnabled = TRUE"
ForEach ($objItem in $colItems)
{
ForEach ($objItem2 in $settings_array)
{
$resultList += $objItem | Select $objItem2
$richTextBox1.Appendtext("$objItem2" + ": " + $objItem.$objItem2 + "`r`n")
$richtextbox1.ScrollToCaret()
}
}
$resultList | export-csv "$ScriptDir\Exports\$Outputfile"
CSV is made with rows and columns. Each object in the array you export gets a row, and each object gets a column value for each property. You add a new record/object to the resultlist with a single property every time(every object has only one property). The reason you only get the first is because your records contain different property-names. To solve this "non-static propertyname" problem, powershell takes the first object's properties as a template for the csv file. Since object2,object3 etc. doesn't include the same property, they will be blank. However, when you hit another object with the same property as the first object had, the value will be included too. Ex. you get the Name property for all network adapters, but blank values on the rest.
Your sample is missing information, ex. how $settings_array is built. If it's a normal string-array like:
$settings_array = "Name", "DisplayName", "Test"
or
$settings_array = #("Name", "DisplayName", "Test")
Then you can pass the whole array to select.
$colItems = GWMI Win32_NetworkAdapterConfiguration -computername $PCname -namespace "root\CimV2" -filter "IpEnabled = TRUE"
ForEach ($objItem in $colItems)
{
#Write to screen
ForEach ($objItem2 in $settings_array)
{
$richTextBox1.Appendtext("$objItem2" + ": " + $objItem.$objItem2 + "`r`n")
$richtextbox1.ScrollToCaret()
}
}
#Save to CSV
$colItems | Select-Object -Property $settings_array | export-csv "$ScriptDir\Exports\$Outputfile"
Notice the last line. Now, the foreach loop is only used for your textbox-content, while the last line formats the CSV as it should.
EDIT Try this to get your settings:
Function GetSettings {
$out = #()
Foreach ($objItem in $chklstGetMIPRet.CheckedItems) {
$out += $objItem.ToString()
}
$out
}
$settings_array = GetSettings
Just a simpe tip: you can export in XML with export-clixml and whatever the number of column, they are all added in the file.

Convert GUID string to octetBytes using PowerShell

I have a powershell script which outputs all Exchange 2003 mailboxes by size.
$computers = "vexch01","vexch02"
foreach ($computer in $computers) {
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Mailbox -computer $computer | sort-object -desc Size | select-object MailboxDisplayName,StoreName,#{Name="Size/Mb";Expression={[math]::round(($_.Size / 1024),2)}}, MailboxGUID | Export-Csv -notype -Path $computer.csv
}
Currently this outputs the MailboxGUID as a string type GUID (e.g. {21EC2020-3AEA-1069-A2DD-08002B30309D}). I want to look up users in AD by this, but AD stores them in octetBytes format.
I have found some powershell functions which will do the conversion but only when the curly braces are removed. The Guid.ToString method should supply this, but I can't get it to work in the above.
However, if I could figure out how to do that, the Guid.ToByteArray method might get me even closer.
Has anyone cracked this?
Update: the answers so far helped me write a function that converts the mailboxguid into the correct format for searching via LDAP. However, I now cannot get this working in the script. This is my updated script:
function ConvertGuidToLdapSearchString(
[parameter(mandatory=$true, position=0)]$Guid
)
{
$guid_object = [System.Guid]$Guid
($guid_object.ToByteArray() | foreach { '\' + $_.ToString('x2') }) -join ''
}
# Gets data through WMI from specified Exchange mailbox servers
$servers = "vexch01","vexch02"
foreach ($server in $servers) {
Get-Wmiobject -namespace root\MicrosoftExchangeV2 -class Exchange_Mailbox -computer $computer | sort-object -desc Size | select-object MailboxDisplayName,StoreName,#{Name="Size/Mb";Expression={[math]::round(($_.Size / 1024),2)}}, #{Name="LDAP Guid";Expression={ConvertGuidToLdapSearchString(MailboxGUID)}} | Export-Csv -notype -Path $server.csv
}
I'm not sure why using the function in the select-object with #{Name="LDAP Guid";Expression={ConvertGuidToLdapSearchString(MailboxGUID)}} doesn't work.
Is there another way of using this function in select-object that will give the string?
In conjunction with Andy Schneider's answer, you may find this function useful:
function Convert-GuidToLdapSearchString(
[parameter(mandatory=$true, position=0)][guid]$Guid
)
{
($Guid.ToByteArray() | foreach { '\' + $_.ToString('x2') }) -join ''
}
(I thought I had a more clever way to do this by adding a ScriptProperty to System.Guid, but I seem to have learned that you can't effectively add members to structs.)
I'm not sure I understand what you are trying to accomplish based on your comment, but I think you may have just left out a $_. Here is a somewhat contrived example that creates an object with a property that is a GUID, then uses select and Convert-GuidToLdapSearchString to convert the format. I hope it helps.
$o = New-Object PSObject -Property #{ GUID = $([Guid]::NewGuid()) }
$o
$o | select #{ Name='SearchString'; Expression={ Convert-GuidToLdapSearchString $_.GUID } }
This is not at all how I had imagined the function being used. I expected you would use it to create an LDAP search clause such as:
$searchString = Convert-GuidToLdapSearchString '{9e76c48b-e764-4f0c-8857-77659108a41e}'
$searcher = [adsisearcher]"(msExchMailboxGuid=$searchString)"
$searcher.FindAll()
Are you casting the string to a GUID ?
$guid = [System.Guid]"{21EC2020-3AEA-1069-A2DD-08002B30309D}"
$guid.ToString()
$guid.ToByteArray()