Powershell New Guy Query

Hi guys,

I’m new to PS and still trying to figure out what is and isn’t possible and how to go about getting my ideas down in code!

I have a simple question that I can’t figure out…

I have a variable with multiple values, say

$Keys = @(
"ServiceType"
"ServiceStatus"
"SupportStatus"
"BackupService"
"Division"
"Department"
)

Now I want to perform a select-object command but using the values from $Keys as the objects I wish to select

So…

$variable | select-object name, fullname, $keys

I know the above code is incorrect but just to give you an idea of what I’m trying to do.

Any help is appreciated.

Many thanks,
Callum

@() = array

@{} = hash

I would recommend picking up a book to understand some of the fundamentals in Powershell. Information in Powershell is typically contained in a PSObject. When you run a Powershell cmdlet, they typically will return a PSObject.

You have standard arrays created with parenthesis:

$array = @("Red", "Green", "Blue")

foreach ($color in $array) {
    $color
}

Then you have hash tables, which is what you are referencing in your post created with curly brackets:

# This is a hash, or if your coming from VBScript, a dictionary object
$hash = @{FName="Jim";LName="Smith"}

$hash.GetEnumerator() | foreach{
    $_.Name # a.k.a Key
    $_.Value
}

Lastly, a PSObject is a combination of an array and hashtable:

$myPSObject = @() #an empty array
#append a new item into the array using the hash above
$myPSObject += New-Object -TypeName PSObject -Property $hash
#append a new item into the array manually
$myPSObject += New-Object -TypeName PSObject -Property @{FName="Julie";LName="Johnson"}

$myPSObject | Select FName

There are many ways to create PSObjects, but in the end they are an array of hash tables. If you need further assistance, it would help to understand what you are trying to accomplish so that we can make a suggestion on which of this methods you should use.

Hi guys,

Many thanks for your replies, I think I have an idea about what I need now.

I need a custom PSobject to store the data that I’m working with.

However the data I’m importing will have all sorts of keys & values, some that I want and some that I don’t.

I need a way to validate the “NoteProperty -name” value against a list of pre-written acceptable values.

e.g. as I work through the data looking to import keys and values, if the key name isn’t an approved name I need to ignore it and move on.

In terms of storing and checking against this pre-approved list of key names, how would I go about doing this when adding data using Add-Member?

Many thanks,
Callum

As with anything PowerShell, there are all sorts of ways to get to what you want, some being more efficient than others. For instance, I typically avoid a bunch of calls to Add-Member since it’s generally not necessary if i collect the data and splat it all over at the time of creation of the object.

But without knowing what you’re collecting or how, it’s hard to make recommendations.

As to your list of valid keys, I would stay away from using $keys since that has meaning in PS. I would probably create the list in the first method below.

#implicit array of text strings
$validKeys = "ServiceType","ServiceStatus","SupportStatus","BackupService",
    "Division","Department"
$validKeys
"-"*40
# using a here-string
$validKeys = @"
ServiceType
ServiceStatus
SupportStatus
BackupService
Division
Department
"@ -split "`r`n"
$validKeys

Can you provide an example of the data and how you would like to filter it? I personally do not like Add-Member for creating objects, there are much easier and cleaner ways to create objects. You can make it complicated:

# We have 3 hash tables with FName and LName, but there are
# additional properties we don't want
$hash1 = @{FName="Jim";LName="Smith"}
$hash2 = @{FName="Julie";LName="Johnson";Foo="Bar"}
$hash3 = @{FName="Sally";LName="Wu";Bar="Foo"}

# Everything that is returned from the loop will be written to $myNewObject
# do a foreach against the hashtables placed in an array
$myNewobject = foreach($hashtable in @($hash1, $hash2, $hash3)) {
    #Generate a blank hashtable for the properties we want
    $props = @{}
    foreach($key in $hashtable.GetEnumerator()) {
        #Use a switch statement with the OR (e.g. pipe) to find FName or LName
        switch -Regex ($key.Name) {
            'FName|LName'{
                # if it is FName or LName, add the name and value to the hashtable
                $props.Add($key.Name,$key.Value)
            }
        } #switch
    } #foreach key in hashtable
    #Generate the new object with the properties which is returned up to $myNewObject
    New-Object -TypeName PSObject -Property $props
} #foreach hashtable

$myNewobject

If the objects are actually hash tables in Powershell, it will actually do the work for you:

# We have 3 hash tables with FName and LName, but there are
# additional properties we don't want
$hash1 = @{FName="Jim";LName="Smith"}
$hash2 = @{FName="Julie";LName="Johnson";Foo="Bar"}
$hash3 = @{FName="Sally";LName="Wu";Bar="Foo"}

$myNewobject2 = foreach($hashtable in @($hash1, $hash2, $hash3)) {
    New-Object -TypeName PSObject -Property $hashtable
}

$myNewobject2

Both examples will produce a PSObject with FName and LName. If each hash had a ‘Foo’ property and you don’t want it, you could simply do:

$myNewObject2 | Select FName, LName

Select will create a new PSObject with only the properties you specied.

To expand on Bob’s post above. I believe this is what you are asking.

$variable = Get-Process
$selectkeys = "ID", "Handles", "CPU"

$variable | Select-Object $selectkeys

Hi Rob and Bob,

Thank you very much for this! :slight_smile:

It’s much appreciated, I’ll need a little time to get my head round it, I’ve only been learning for a week.

Bob, I’ve used your simple multi string variable idea as a check list, thanks for that.

Rob, your advice on filtering hash tables makes a lot of sense, I’ll keep that in mind as I try to build my array of hash tables.

In essence the data I’m dealing with is meta data from VMware vCloud so key/value pairs. Unfortunately they are all over the place at the moment so that’s why I’m looking to match against approved key/values and report on them if they don’t.

If the key is valid, then the value is checked, if that’s valid then they both need get stored along with some other data in a hash table in a PSObject. Rinse and repeat per account in vCloud (vCloud meta data is stored as accountname, key, value).

The end goal is to spit out a HTML file for use on a web server.

I’ll give creating the PSObject a go and see how it goes, the validation I think was the hardest part…so far anyway.

Thanks again,
Callum

and if you want to add a property to select while still using your previously defined properties, you can do something like this.

$variable = Get-Process
$selectkeys = "ID", "Handles", "CPU"

$variable | Select-Object (@("ProcessName") + $selectkeys)

Hi Curtis,

That’s great info, thank you.

Could you explain a little around:
$variable | Select-Object (@(“ProcessName”) + $selectkeys)

I understand what you are saying I just don’t understand why it would have to be in that format.

I guess I need a better grasp on what the objects are made up from and how you work with them.

Many thanks,
Callum

The simple response is you are adding an Array to another Array to make a longer Array and then passing that as a parameter to Select-Object, but let’s walk through that to validate what is happening.

If we look at the following

PS C:\> $selectkeys = "Position0", "Position1", "Position2" PS C:\> $selectkeys Position0 Position1 Position2

PS C:> $selectkeys.GetType()

IsPublic IsSerial Name BaseType


True True Object System.Array

We see that PowerShell is interpreting the command as setting an array of strings to the variable $selectkeys. This is the same as typing it as an array literal

PS C:\> $selectkeys = @("Position0", "Position1", "Position2") PS C:\> $selectkeys Position0 Position1 Position2

PS C:> $selectkeys.GetType()

IsPublic IsSerial Name BaseType


True True Object System.Array

If we look at each position in the array we see our individual values, and see that position 3 has no value

PS C:\> $selectkeys[0] Position0 PS C:\> $selectkeys[1] Position1 PS C:\> $selectkeys[2] Position2 PS C:\> $selectkeys[3] PS C:\>

If we create another array like we did in the beginning, we can add that to our existing array that is stored in the $selectkeys variable to make a longer array. Note: where the new entries are added is determined by which side of the “+” sign they are placed on.

PS C:\> "Position3", "Position4" + $selectkeys Position3 Position4 Position0 Position1 Position2 PS C:\>

PS C:> $selectkeys + “Position3”, “Position4”
Position0
Position1
Position2
Position3
Position4

Where this can get you in trouble is if you are only adding one additional string to the array

PS C:\> "Position3" + $selectkeys Position3Position0 Position1 Position2

PS C:> (“Position3” + $selectkeys).GetType()

IsPublic IsSerial Name BaseType


True True String System.Object

As you can see the result is now a string rather than your previous array. This is because PowerShell will interpret a single string in quotes as a string object rather than an array of string objects. In order to add a single string to the array, we have to help PowerShell by telling it this is an array using an array literal. PowerShell can then properly add the new array values with the existing array. Technically, the way the statement is structured, @(“Position3”) is the array it starts with and adds all of the values from the array stored in $selectkeys to it. The result is that even though the string says “Position3” it is actually in Position 0 in the array.

PS C:\> @("Position3") + $selectkeys Position3 Position0 Position1 Position2

PS C:> (@(“Position3”) + $selectkeys).GetType()

IsPublic IsSerial Name BaseType


True True Object System.Array

So now you see what is happening with @(“ProcessName”) + $selectkeys, but there is still the additional () around it. Those parenthesis simple tell powershell to process the @(“ProcessName”) + $selectkeys code first and then return the results of that processing as a single parameter to Select-Object. Otherwise, Select-Object would see a spaces between the “+” the arrays and think it is looking at 3 parameters rather than one.

Select-Object (@("ProcessName") + $selectkeys)

Hope that helps.

Curtis,

Your explanation is fantastic, I feel like I have a better grasp on things, thank you very much!

I’ll be saving that response as I’m sure it will come in useful again.

Many thanks,
Callum