Powershell New Guy Query

This topic contains 11 replies, has 5 voices, and was last updated by Profile photo of Callum Shackleton Callum Shackleton 1 year, 6 months ago.

  • Author
    Posts
  • #29460
    Profile photo of Callum Shackleton
    Callum Shackleton
    Participant

    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

  • #29464
    Profile photo of Dan Potter
    Dan Potter
    Participant

    @() = array

    @{} = hash

  • #29466
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    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.

  • #29468
    Profile photo of Callum Shackleton
    Callum Shackleton
    Participant

    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

  • #29473
    Profile photo of Bob McCoy
    Bob McCoy
    Participant

    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
    
  • #29477
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    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.

  • #29491
    Profile photo of Curtis Smith
    Curtis Smith
    Participant

    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
    
  • #29492
    Profile photo of Callum Shackleton
    Callum Shackleton
    Participant

    Hi Rob and Bob,

    Thank you very much for this! 🙂

    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

  • #29495
    Profile photo of Curtis Smith
    Curtis Smith
    Participant

    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)
    
  • #29520
    Profile photo of Callum Shackleton
    Callum Shackleton
    Participant

    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

  • #29540
    Profile photo of Curtis Smith
    Curtis Smith
    Participant

    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.

  • #29572
    Profile photo of Callum Shackleton
    Callum Shackleton
    Participant

    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

You must be logged in to reply to this topic.